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