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