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