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