bc7ea94ff34b65b9f0a9b1ea368e0b1925504f40
[openafs.git] / src / audit / audit.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13
14 #include <fcntl.h>
15 #include <stdarg.h>
16 #include <string.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #ifndef AFS_NT40_ENV
20 #include <unistd.h>
21 #else
22 #include <io.h>
23 #endif
24 #ifdef AFS_AIX32_ENV
25 #include <sys/audit.h>
26 #else
27 #define AUDIT_OK 0
28 #define AUDIT_FAIL 1
29 #define AUDIT_FAIL_AUTH 2
30 #define AUDIT_FAIL_ACCESS 3
31 #define AUDIT_FAIL_PRIV 4
32 #endif /* AFS_AIX32_ENV */
33 #include <errno.h>
34
35 #include "afs/afsint.h"
36 #include <rx/rx.h>
37 #include <rx/rxkad.h>
38 #include "audit.h"
39 #include "lock.h"
40 #ifdef AFS_AIX32_ENV
41 #include <sys/audit.h>
42 #endif
43 #include <afs/afsutil.h>
44
45 /* C99 requires va_copy.  Older versions of GCC provide __va_copy.  Per t
46    Autoconf manual, memcpy is a generally portable fallback. */          
47 #ifndef va_copy              
48 # ifdef __va_copy
49 #  define va_copy(d, s)         __va_copy((d), (s))             
50 # else
51 #  define va_copy(d, s)         memcpy(&(d), &(s), sizeof(va_list)) 
52 # endif
53 #endif      
54
55 char *bufferPtr;
56 int bufferLen;
57 int osi_audit_all = (-1);       /* Not determined yet */
58 int osi_echo_trail = (-1);
59
60 FILE *auditout = NULL;
61
62 int osi_audit_check(void);
63
64 #ifdef AFS_AIX32_ENV
65 static void
66 audmakebuf(char *audEvent, va_list vaList)
67 {
68 #ifdef AFS_AIX32_ENV
69     int code;
70 #endif
71     int vaEntry;
72     int vaInt;
73     afs_int32 vaLong;
74     char *vaStr;
75     struct AFSFid *vaFid;
76
77     vaEntry = va_arg(vaList, int);
78     while (vaEntry != AUD_END) {
79         switch (vaEntry) {
80         case AUD_STR:           /* String */
81         case AUD_NAME:          /* Name */
82         case AUD_ACL:           /* ACL */
83             vaStr = (char *)va_arg(vaList, char *);
84             if (vaStr) {
85                 strcpy(bufferPtr, vaStr);
86                 bufferPtr += strlen(vaStr) + 1;
87             } else {
88                 strcpy(bufferPtr, "");
89                 bufferPtr++;
90             }
91             break;
92         case AUD_INT:           /* Integer */
93         case AUD_ID:            /* ViceId */
94             vaInt = va_arg(vaList, int);
95             *(int *)bufferPtr = vaInt;
96             bufferPtr += sizeof(vaInt);
97             break;
98         case AUD_DATE:          /* Date    */
99         case AUD_HOST:          /* Host ID */
100         case AUD_LONG:          /* long    */
101             vaLong = va_arg(vaList, afs_int32);
102             *(afs_int32 *) bufferPtr = vaLong;
103             bufferPtr += sizeof(vaLong);
104             break;
105         case AUD_FID:           /* AFSFid - contains 3 entries */
106             vaFid = (struct AFSFid *)va_arg(vaList, struct AFSFid *);
107             if (vaFid) {
108                 memcpy(bufferPtr, vaFid, sizeof(struct AFSFid));
109             } else {
110                 memset(bufferPtr, 0, sizeof(struct AFSFid));
111             }
112             bufferPtr += sizeof(struct AFSFid);
113             break;
114
115             /* Whole array of fids-- don't know how to handle variable length audit
116              * data with AIX audit package, so for now we just store the first fid.
117              * Better one than none. */
118         case AUD_FIDS:
119             {
120                 struct AFSCBFids *Fids;
121
122                 Fids = (struct AFSCBFids *)va_arg(vaList, struct AFSCBFids *);
123                 if (Fids && Fids->AFSCBFids_len) {
124                     *((u_int *) bufferPtr) = Fids->AFSCBFids_len;
125                     bufferPtr += sizeof(u_int);
126                     memcpy(bufferPtr, Fids->AFSCBFids_val,
127                            sizeof(struct AFSFid));
128                 } else {
129                     *((u_int *) bufferPtr) = 0;
130                     bufferPtr += sizeof(u_int);
131                     memset(bufferPtr, 0, sizeof(struct AFSFid));
132                 }
133                 bufferPtr += sizeof(struct AFSFid);
134                 break;
135             }
136         default:
137 #ifdef AFS_AIX32_ENV
138             code =
139                 auditlog("AFS_Aud_EINVAL", (-1), audEvent,
140                          (strlen(audEvent) + 1));
141 #endif
142             return;
143             break;
144         }                       /* end switch */
145
146         vaEntry = va_arg(vaList, int);
147     }                           /* end while */
148 }
149 #endif
150
151 static void
152 printbuf(FILE *out, int rec, char *audEvent, char *afsName, afs_int32 hostId, 
153          afs_int32 errCode, va_list vaList)
154 {
155     int vaEntry;
156     int vaInt;
157     afs_int32 vaLong;
158     char *vaStr;
159     struct AFSFid *vaFid;
160     struct AFSCBFids *vaFids;
161     int num = LogThreadNum();
162     struct in_addr hostAddr;
163     time_t currenttime;
164     char *timeStamp;
165     char tbuffer[26];
166     
167     /* Don't print the timestamp or thread id if we recursed */
168     if (rec == 0) {
169         currenttime = time(0);
170         timeStamp = afs_ctime(&currenttime, tbuffer,
171                               sizeof(tbuffer));
172         timeStamp[24] = ' ';   /* ts[24] is the newline, 25 is the null */
173         fprintf(out, timeStamp);
174
175         if (num > -1)
176             fprintf(out, "[%d] ", num);
177     }
178     
179     fprintf(out,  "EVENT %s CODE %d ", audEvent, errCode);
180
181     if (afsName) {
182         hostAddr.s_addr = hostId;
183         fprintf(out,  "NAME %s HOST %s ", afsName, inet_ntoa(hostAddr));
184     }
185
186     vaEntry = va_arg(vaList, int);
187     while (vaEntry != AUD_END) {
188         switch (vaEntry) {
189         case AUD_STR:           /* String */
190             vaStr = (char *)va_arg(vaList, char *);
191             if (vaStr)
192                 fprintf(out,  "STR %s ", vaStr);
193             else
194                 fprintf(out,  "STR <null>");
195             break;
196         case AUD_NAME:          /* Name */
197             vaStr = (char *)va_arg(vaList, char *);
198             if (vaStr)
199                 fprintf(out,  "NAME %s ", vaStr);
200             else
201                 fprintf(out,  "NAME <null>");
202             break;
203         case AUD_ACL:           /* ACL */
204             vaStr = (char *)va_arg(vaList, char *);
205             if (vaStr)
206                 fprintf(out,  "ACL %s ", vaStr);
207             else
208                 fprintf(out,  "ACL <null>");
209             break;
210         case AUD_INT:           /* Integer */
211             vaInt = va_arg(vaList, int);
212             fprintf(out,  "INT %d ", vaInt);
213             break;
214         case AUD_ID:            /* ViceId */
215             vaInt = va_arg(vaList, int);
216             fprintf(out,  "ID %d ", vaInt);
217             break;
218         case AUD_DATE:          /* Date    */
219             vaLong = va_arg(vaList, afs_int32);
220             fprintf(out, "DATE %u ", vaLong);
221             break;
222         case AUD_HOST:          /* Host ID */
223             vaLong = va_arg(vaList, afs_int32);
224             hostAddr.s_addr = vaLong;
225             fprintf(out, "HOST %s ", inet_ntoa(hostAddr));
226             break;
227         case AUD_LONG:          /* afs_int32    */
228             vaLong = va_arg(vaList, afs_int32);
229             fprintf(out, "LONG %d ", vaLong);
230             break;
231         case AUD_FID:           /* AFSFid - contains 3 entries */
232             vaFid = va_arg(vaList, struct AFSFid *);
233             if (vaFid)
234                 fprintf(out, "FID %u:%u:%u ", vaFid->Volume, vaFid->Vnode,
235                        vaFid->Unique);
236             else
237                 fprintf(out, "FID %u:%u:%u ", 0, 0, 0);
238             break;
239         case AUD_FIDS:          /* array of Fids */
240             vaFids = va_arg(vaList, struct AFSCBFids *);
241
242             if (vaFids) {
243                 int i;
244                 
245                 vaFid = vaFids->AFSCBFids_val;
246                 
247                 if (vaFid) {
248                     fprintf(out, "FIDS %u FID %u:%u:%u ", vaFids->AFSCBFids_len, vaFid->Volume,
249                              vaFid->Vnode, vaFid->Unique);
250                     for ( i = 1; i < vaFids->AFSCBFids_len; i++, vaFid++ ) 
251                         fprintf(out, "FID %u:%u:%u ", vaFid->Volume,
252                                 vaFid->Vnode, vaFid->Unique);
253                 } else
254                     fprintf(out, "FIDS 0 FID 0:0:0 ");
255
256             }
257             break;
258         default:
259             fprintf(out, "--badval-- ");
260             break;
261         }                       /* end switch */
262         vaEntry = va_arg(vaList, int);
263     }                           /* end while */
264
265     fprintf(out, "\n");
266 }
267
268 #ifdef AFS_PTHREAD_ENV
269 static pthread_mutex_t audit_lock;
270 static volatile afs_int32   audit_lock_initialized = 0;
271 static pthread_once_t audit_lock_once = PTHREAD_ONCE_INIT;
272
273 static void
274 osi_audit_init_lock(void)
275 {
276     pthread_mutex_init(&audit_lock, NULL);
277     audit_lock_initialized = 1;
278 }
279 #endif
280
281 void
282 osi_audit_init(void)
283 {
284 #ifdef AFS_PTHREAD_ENV
285     if (!audit_lock_initialized) {
286         pthread_once(&audit_lock_once, osi_audit_init_lock);
287     }
288 #endif /* AFS_PTHREAD_ENV */
289 }
290
291 /* ************************************************************************** */
292 /* The routine that acually does the audit call.
293  * ************************************************************************** */
294 int
295 osi_audit_internal(char *audEvent,      /* Event name (15 chars or less) */
296                    afs_int32 errCode,   /* The error code */
297                    char *afsName,
298                    afs_int32 hostId,
299                    va_list vaList)
300 {
301 #ifdef AFS_AIX32_ENV
302     afs_int32 code;
303     afs_int32 err;
304     static char BUFFER[32768];
305 #endif
306     int result;
307     va_list vaCopy;
308
309 #ifdef AFS_PTHREAD_ENV
310     /* i'm pretty sure all the server apps now call osi_audit_init(),
311      * but to be extra careful we'll leave this assert in here for a 
312      * while to make sure */
313     assert(audit_lock_initialized);
314 #endif /* AFS_PTHREAD_ENV */
315
316     if ((osi_audit_all < 0) || (osi_echo_trail < 0))
317         osi_audit_check();
318     if (!osi_audit_all && !auditout)
319         return 0;
320
321     va_copy(vaCopy, vaList);
322
323     switch (errCode) {
324     case 0:
325         result = AUDIT_OK;
326         break;
327     case KANOAUTH:              /* kautils.h   */
328     case RXKADNOAUTH:           /* rxkad.h     */
329         result = AUDIT_FAIL_AUTH;
330         break;
331     case EPERM:         /* errno.h     */
332     case EACCES:                /* errno.h     */
333     case PRPERM:                /* pterror.h   */
334         result = AUDIT_FAIL_ACCESS;
335         break;
336     case VL_PERM:               /* vlserver.h  */
337     case BUDB_NOTPERMITTED:     /* budb_errs.h */
338     case BZACCESS:              /* bnode.h     */
339     case VOLSERBAD_ACCESS:      /* volser.h    */
340         result = AUDIT_FAIL_PRIV;
341         break;
342     default:
343         result = AUDIT_FAIL;
344         break;
345     }
346
347 #ifdef AFS_PTHREAD_ENV
348     pthread_mutex_lock(&audit_lock);
349 #endif
350 #ifdef AFS_AIX32_ENV
351     bufferPtr = BUFFER;
352
353     /* Put the error code into the buffer list */
354     *(int *)bufferPtr = errCode;
355     bufferPtr += sizeof(errCode);
356
357     audmakebuf(audEvent, vaList);
358 #endif
359
360     if (osi_echo_trail) {
361         printbuf(stdout, 0, audEvent, afsName, hostId, errCode, vaList);
362     }
363     va_end(vaCopy);
364
365 #ifdef AFS_AIX32_ENV
366     bufferLen = (int)((afs_int32) bufferPtr - (afs_int32) & BUFFER[0]);
367     code = auditlog(audEvent, result, BUFFER, bufferLen);
368 #else
369     if (auditout) {
370         printbuf(auditout, 0, audEvent, afsName, hostId, errCode, vaList);
371         fflush(auditout);
372     }
373 #endif
374 #ifdef AFS_PTHREAD_ENV
375     pthread_mutex_unlock(&audit_lock);
376 #endif
377
378     return 0;
379 }
380 int
381 osi_audit(char *audEvent,       /* Event name (15 chars or less) */
382           afs_int32 errCode,    /* The error code */
383           ...)
384 {
385     va_list vaList;
386
387     if ((osi_audit_all < 0) || (osi_echo_trail < 0))
388         osi_audit_check();
389     if (!osi_audit_all && !auditout)
390         return 0;
391
392     va_start(vaList, errCode);
393     osi_audit_internal(audEvent, errCode, NULL, 0, vaList);
394     va_end(vaList);
395
396     return 0;
397 }
398
399 /* ************************************************************************** */
400 /* Given a RPC call structure, this routine extracts the name and host id from the 
401  * call and includes it within the audit information.
402  * ************************************************************************** */
403 int
404 osi_auditU(struct rx_call *call, char *audEvent, int errCode, ...)
405 {
406     struct rx_connection *conn;
407     struct rx_peer *peer;
408     afs_int32 secClass;
409     afs_int32 code;
410     char afsName[MAXKTCNAMELEN];
411     afs_int32 hostId;
412     va_list vaList;
413
414     if (osi_audit_all < 0)
415         osi_audit_check();
416     if (!osi_audit_all && !auditout)
417         return 0;
418
419     strcpy(afsName, "--Unknown--");
420     hostId = 0;
421
422     if (call) {
423         conn = rx_ConnectionOf(call);   /* call -> conn) */
424         if (conn) {
425             secClass = rx_SecurityClassOf(conn);        /* conn -> securityIndex */
426             if (secClass == 0) {        /* unauthenticated */
427                 osi_audit("AFS_Aud_Unauth", (-1), AUD_STR, audEvent, AUD_END);
428                 strcpy(afsName, "--UnAuth--");
429             } else if (secClass == 2) { /* authenticated */
430                 char tcell[MAXKTCREALMLEN];
431                 char name[MAXKTCNAMELEN];
432                 char inst[MAXKTCNAMELEN];
433                 char vname[256];
434                 int  ilen, clen;
435
436                 code =
437                     rxkad_GetServerInfo(conn, NULL, NULL, name, inst, tcell,
438                                         NULL);
439                 if (code) {
440                     osi_audit("AFS_Aud_NoAFSId", (-1), AUD_STR, audEvent, AUD_END);
441                     strcpy(afsName, "--NoName--");
442                 } else {
443                     strncpy(vname, name, sizeof(vname));
444                     if ((ilen = strlen(inst))) {
445                         if (strlen(vname) + 1 + ilen >= sizeof(vname))
446                             goto done;
447                         strcat(vname, ".");
448                         strcat(vname, inst);
449                     }
450                     if ((clen = strlen(tcell))) {
451 #if defined(AFS_ATHENA_STDENV) || defined(AFS_KERBREALM_ENV)
452                         static char local_realms[AFS_NUM_LREALMS][AFS_REALM_SZ];
453                         static int  num_lrealms = -1;
454                         int i, lrealm_match;
455
456                         if (num_lrealms == -1) {
457                             for (i=0; i<AFS_NUM_LREALMS; i++) {
458                                 if (afs_krb_get_lrealm(local_realms[i], i) != 0 /*KSUCCESS*/)
459                                     break;
460                             }
461
462                             if (i == 0)
463                                 strncpy(local_realms[0], "UNKNOWN.LOCAL.REALM", AFS_REALM_SZ);
464                             num_lrealms = i;
465                         }
466
467                         /* Check to see if the ticket cell matches one of the local realms */
468                         lrealm_match = 0;
469                         for ( i=0;i<num_lrealms;i++ ) {
470                             if (!strcasecmp(local_realms[i], tcell)) {
471                                 lrealm_match = 1;
472                                 break;
473                             }
474                         }
475                         /* If yes, then make sure that the name is not present in 
476                          * an exclusion list */
477                         if (lrealm_match) {
478                             char uname[256];
479                             if (inst[0])
480                                 snprintf(uname,sizeof(uname),"%s.%s@%s",name,inst,tcell);
481                             else
482                                 snprintf(uname,sizeof(uname),"%s@%s",name,tcell);
483
484                             if (afs_krb_exclusion(uname))
485                                 lrealm_match = 0;
486                         }
487
488                         if (!lrealm_match) {    
489                             if (strlen(vname) + 1 + clen >= sizeof(vname))
490                                 goto done;
491                             strcat(vname, "@");
492                             strcat(vname, tcell);
493                         }
494 #endif
495                     }
496                     strcpy(afsName, vname);
497                 }
498             } else {            /* Unauthenticated & unknown */
499                 osi_audit("AFS_Aud_UnknSec", (-1), AUD_STR, audEvent, AUD_END);
500                 strcpy(afsName, "--Unknown--");
501             }
502         done:
503             peer = rx_PeerOf(conn);     /* conn -> peer */
504             if (peer)
505                 hostId = rx_HostOf(peer);       /* peer -> host */
506             else
507                 osi_audit("AFS_Aud_NoHost", (-1), AUD_STR, audEvent, AUD_END);
508         } else {                /* null conn */
509             osi_audit("AFS_Aud_NoConn", (-1), AUD_STR, audEvent, AUD_END);
510         }
511     } else {                    /* null call */
512         osi_audit("AFS_Aud_NoCall", (-1), AUD_STR, audEvent, AUD_END);
513     }
514     va_start(vaList, errCode);
515     osi_audit_internal(audEvent, errCode, afsName, hostId, vaList);
516     va_end(vaList);
517     return 0;
518 }
519
520 /* ************************************************************************** */
521 /* Determines whether auditing is on or off by looking at the Audit file.
522  * If the string AFS_AUDIT_AllEvents is defined in the file, then auditing will be 
523  * enabled.
524  * ************************************************************************** */
525
526 int
527 osi_audit_check(void)
528 {
529     FILE *fds;
530     int onoff;
531     char event[257];
532
533     osi_audit_all = 1;          /* say we made check (>= 0) */
534     /* and assume audit all events (for now) */
535     onoff = 0;                  /* assume we will turn auditing off */
536     osi_echo_trail = 0;         /* assume no echoing   */
537
538     fds = fopen(AFSDIR_SERVER_AUDIT_FILEPATH, "r");
539     if (fds) {
540         while (fscanf(fds, "%256s", event) > 0) {
541             if (strcmp(event, "AFS_AUDIT_AllEvents") == 0)
542                 onoff = 1;
543
544             if (strcmp(event, "Echo_Trail") == 0)
545                 osi_echo_trail = 1;
546         }
547         fclose(fds);
548     }
549
550     /* Audit this event all of the time */
551     if (onoff)
552         osi_audit("AFS_Aud_On", 0, AUD_END);
553     else
554         osi_audit("AFS_Aud_Off", 0, AUD_END);
555
556     /* Now set whether we audit all events from here on out */
557     osi_audit_all = onoff;
558
559     return 0;
560 }
561
562 int
563 osi_audit_file(char *fileName)
564 {
565     int tempfd, flags;
566     char oldName[MAXPATHLEN];
567     
568 #ifndef AFS_NT40_ENV
569     struct stat statbuf;
570     
571     if ((lstat(fileName, &statbuf) == 0)
572         && (S_ISFIFO(statbuf.st_mode))) {
573         flags = O_WRONLY | O_NONBLOCK;
574     } else 
575 #endif
576     {
577         strcpy(oldName, fileName);
578         strcat(oldName, ".old");
579         renamefile(fileName, oldName);
580         flags = O_WRONLY | O_TRUNC | O_CREAT;
581     }
582     tempfd = open(fileName, flags, 0666);
583     if (tempfd > -1) {
584         auditout = fdopen(tempfd, "a");
585         if (!auditout) {
586             printf("Warning: auditlog %s not writable, ignored.\n", fileName);
587             return 1;
588         }
589     } else { 
590         printf("Warning: auditlog %s not writable, ignored.\n", fileName);
591         return 1;
592     }
593     return 0;
594 }