07b9d9bc3b4231a66824f0a7ff911abf19287eca
[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 #include <roken.h>
14
15 #ifdef AFS_AIX32_ENV
16 #include <sys/audit.h>
17 #else
18 #define AUDIT_OK 0
19 #define AUDIT_FAIL 1
20 #define AUDIT_FAIL_AUTH 2
21 #define AUDIT_FAIL_ACCESS 3
22 #define AUDIT_FAIL_PRIV 4
23 #endif /* AFS_AIX32_ENV */
24
25 #include <afs/opr.h>
26 #include "afs/afsint.h"
27 #include "afs/butc.h"
28 #include <rx/rx.h>
29 #include <rx/rxkad.h>
30 #include "audit.h"
31 #include "audit-api.h"
32 #include "lock.h"
33
34 #include <afs/afsutil.h>
35
36 extern struct osi_audit_ops audit_file_ops;
37 #ifdef HAVE_SYS_IPC_H
38 extern struct osi_audit_ops audit_sysvmq_ops;
39 #endif
40
41 static struct {
42     void *rock;
43     int (*islocal)(void *rock, char *name, char *inst, char *cell);
44 } audit_user_check = { NULL, NULL };
45
46 static struct {
47     const char *name;
48     const struct osi_audit_ops *ops;
49 } audit_interfaces[] = {
50
51     { "file", &audit_file_ops },
52 #ifdef HAVE_SYS_IPC_H
53     { "sysvmq", &audit_sysvmq_ops },
54 #endif
55 };
56 /* default_interface indexes into the audit_interfaces list. */
57 static int default_interface = 0; /* Set default to the file interface */
58
59 #define N_INTERFACES (sizeof(audit_interfaces) / sizeof(audit_interfaces[0]))
60
61 /*
62  * Audit interface calling sequence:
63  * osi_audit_interface - sets the default audit interface
64  * osi_audit_file
65  *    create_instance - Called during command arg processing (-auditlog)
66  *    open_file - Called during command arg processing (-auditlog)
67  * osi_audit_open_interface
68  *    open_interface - Called after thread environment has been established
69  * osi_audit_close_interface
70  *    close_interface - Called during main process shutdown
71  * osi_audit
72  *    send_msg - Called during audit events
73  */
74 struct audit_log {
75     struct opr_queue link;
76     const struct osi_audit_ops *audit_ops;
77     int auditout_open;
78     void *context;
79 };
80
81 struct audit_msg {
82     char buf[OSI_AUDIT_MAXMSG];
83     size_t len; /* length of the string in 'buf' (not including the trailing '\0') */
84     int truncated; /* was this message truncated during formatting? */
85 };
86
87 #ifdef AFS_PTHREAD_ENV
88 static pthread_mutex_t audit_lock;
89 static pthread_once_t audit_lock_once = PTHREAD_ONCE_INIT;
90 #endif
91
92 /* Chain of active interfaces */
93 static struct opr_queue audit_logs = {&audit_logs, &audit_logs};
94
95 static int osi_audit_all = (-1);        /* Not determined yet */
96 static int osi_echo_trail = (-1);
97
98 static int auditout_open = 0;           /* True if any interface is open */
99
100 static int osi_audit_check(void);
101
102 /*
103  * Send the message to all the interfaces
104  * @pre audit_lock held
105  */
106 static void
107 multi_send_msg(struct audit_msg *msg)
108 {
109     struct opr_queue *cursor;
110
111     if (msg->len < 1) /* Don't send empty strings */
112         return;
113
114     for (opr_queue_Scan(&audit_logs, cursor)) {
115         struct audit_log *alog;
116         alog = opr_queue_Entry(cursor, struct audit_log, link);
117         if (alog->auditout_open) {
118             alog->audit_ops->send_msg(alog->context, msg->buf, msg->len,
119                                       msg->truncated);
120         }
121     }
122 }
123 static void
124 append_msg(struct audit_msg *msg, const char *format, ...)
125 {
126     va_list ap;
127     int remaining, printed;
128
129     if (msg->truncated) {
130         return;
131     }
132
133     if (msg->len >= OSI_AUDIT_MAXMSG - 1) {
134         /* Make sure we have at least some space left */
135         msg->truncated = 1;
136         return;
137     }
138
139     remaining = OSI_AUDIT_MAXMSG - msg->len;
140
141     va_start(ap, format);
142
143     printed = vsnprintf(&msg->buf[msg->len], remaining, format, ap);
144
145     if (printed < 0) {
146         /* Error during formatting. */
147         msg->truncated = 1;
148         printed = 0;
149
150     } else if (printed >= remaining) {
151         /* We tried to write more characters than we had space for. */
152         msg->truncated = 1;
153         printed = remaining - 1;
154
155         /* Make sure we're still terminated. */
156         msg->buf[OSI_AUDIT_MAXMSG - 1] = '\0';
157     }
158
159     msg->len += printed;
160     opr_Assert(msg->len < OSI_AUDIT_MAXMSG);
161
162     va_end(ap);
163 }
164 #ifdef AFS_AIX32_ENV
165 static char *bufferPtr;
166 static int bufferLen;
167
168 static void
169 audmakebuf(char *audEvent, va_list vaList)
170 {
171     int code;
172     int vaEntry;
173     int vaInt;
174     afs_int32 vaLong;
175     char *vaStr;
176     struct AFSFid *vaFid;
177
178     vaEntry = va_arg(vaList, int);
179     while (vaEntry != AUD_END) {
180         switch (vaEntry) {
181         case AUD_STR:           /* String */
182         case AUD_NAME:          /* Name */
183         case AUD_ACL:           /* ACL */
184             vaStr = (char *)va_arg(vaList, char *);
185             if (vaStr) {
186                 strcpy(bufferPtr, vaStr);
187                 bufferPtr += strlen(vaStr) + 1;
188             } else {
189                 strcpy(bufferPtr, "");
190                 bufferPtr++;
191             }
192             break;
193         case AUD_INT:           /* Integer */
194         case AUD_ID:            /* ViceId */
195             vaInt = va_arg(vaList, int);
196             *(int *)bufferPtr = vaInt;
197             bufferPtr += sizeof(vaInt);
198             break;
199         case AUD_DATE:          /* Date    */
200         case AUD_HOST:          /* Host ID */
201         case AUD_LONG:          /* long    */
202             vaLong = va_arg(vaList, afs_int32);
203             *(afs_int32 *) bufferPtr = vaLong;
204             bufferPtr += sizeof(vaLong);
205             break;
206         case AUD_FID:           /* AFSFid - contains 3 entries */
207             vaFid = (struct AFSFid *)va_arg(vaList, struct AFSFid *);
208             if (vaFid) {
209                 memcpy(bufferPtr, vaFid, sizeof(struct AFSFid));
210             } else {
211                 memset(bufferPtr, 0, sizeof(struct AFSFid));
212             }
213             bufferPtr += sizeof(struct AFSFid);
214             break;
215
216             /* Whole array of fids-- don't know how to handle variable length audit
217              * data with AIX audit package, so for now we just store the first fid.
218              * Better one than none. */
219         case AUD_FIDS:
220             {
221                 struct AFSCBFids *Fids;
222
223                 Fids = (struct AFSCBFids *)va_arg(vaList, struct AFSCBFids *);
224                 if (Fids && Fids->AFSCBFids_len) {
225                     *((u_int *) bufferPtr) = Fids->AFSCBFids_len;
226                     bufferPtr += sizeof(u_int);
227                     memcpy(bufferPtr, Fids->AFSCBFids_val,
228                            sizeof(struct AFSFid));
229                 } else {
230                     *((u_int *) bufferPtr) = 0;
231                     bufferPtr += sizeof(u_int);
232                     memset(bufferPtr, 0, sizeof(struct AFSFid));
233                 }
234                 bufferPtr += sizeof(struct AFSFid);
235                 break;
236             }
237         /* butc tape label */
238         case AUD_TLBL:
239             {
240                 struct tc_tapeLabel *label;
241
242                 label = (struct tc_tapeLabel *)va_arg(vaList,
243                                                       struct tc_tapeLabel *);
244                 if (label)
245                     memcpy(bufferPtr, label, sizeof(*label));
246                 else
247                     memset(bufferPtr, 0, sizeof(*label));
248                 bufferPtr += sizeof(label);
249                 break;
250             }
251         /* butc dump interface */
252         case AUD_TDI:
253             {
254                 struct tc_dumpInterface *di;
255
256                 di = (struct tc_dumpInterface *)
257                         va_arg(vaList, struct tc_dumpInterface *);
258                 if (di)
259                     memcpy(bufferPtr, di, sizeof(*di));
260                 else
261                     memset(bufferPtr, 0, sizeof(*di));
262                 bufferPtr += sizeof(*di);
263                 break;
264             }
265         /*
266          * butc dump array
267          * An array of dump descriptions, but the AIX audit package assumes fixed
268          * length, so we can only do the first one for now.
269          */
270         case AUD_TDA:
271             {
272                 struct tc_dumpArray *da;
273
274                 da = (struct tc_dumpArray *)
275                         va_arg(vaList, struct tc_dumpArray *);
276                 if (da && da->tc_dumpArray_len) {
277                     memcpy(bufferPtr, &da->tc_dumpArray_len, sizeof(u_int));
278                     bufferPtr += sizeof(u_int);
279                     memcpy(bufferPtr, da->tc_dumpArray_val,
280                            sizeof(da->tc_dumpArray_val[0]));
281                 } else {
282                     memset(bufferPtr, 0, sizeof(u_int));
283                     bufferPtr += sizeof(u_int);
284                     memset(bufferPtr, 0, sizeof(da->tc_dumpArray_val[0]));
285                 }
286                 bufferPtr += sizeof(da->tc_dumpArray_val[0]);
287                 break;
288             }
289         /*
290          * butc restore array
291          * An array of restore descriptions, but the AIX audit package assumes
292          * fixed length, so we can only do the first one for now.
293          */
294         case AUD_TRA:
295             {
296                 struct tc_restoreArray *ra;
297
298                 ra = (struct tc_restoreArray *)
299                         va_arg(vaList, struct tc_restoreArray *);
300                 if (ra && ra->tc_restoreArray_len) {
301                     memcpy(bufferPtr, &ra->tc_restoreArray_len, sizeof(u_int));
302                     bufferPtr += sizeof(u_int);
303                     memcpy(bufferPtr, ra->tc_restoreArray_val,
304                            sizeof(ra->tc_restoreArray_val[0]));
305                 } else {
306                     memset(bufferPtr, 0, sizeof(u_int));
307                     bufferPtr += sizeof(u_int);
308                     memset(bufferPtr, 0, sizeof(ra->tc_restoreArray_val[0]));
309                 }
310                 bufferPtr += sizeof(ra->tc_restoreArray_val[0]);
311                 break;
312             }
313         /* butc tape controller status */
314             {
315                 struct tciStatusS *status;
316
317                 status = (struct tciStatusS *)va_arg(vaList,
318                                                      struct tciStatusS *);
319                 if (status)
320                     memcpy(bufferPtr, status, sizeof(*status));
321                 else
322                     memset(bufferPtr, 0, sizeof(*status));
323                 bufferPtr += sizeof(*status);
324                 break;
325             }
326         default:
327 #ifdef AFS_AIX32_ENV
328             code =
329                 auditlog("AFS_Aud_EINVAL", (-1), audEvent,
330                          (strlen(audEvent) + 1));
331 #endif
332             return;
333             break;
334         }                       /* end switch */
335
336         vaEntry = va_arg(vaList, int);
337     }                           /* end while */
338 }
339 #endif
340
341 static void
342 printbuf(int rec, char *audEvent, char *afsName, afs_int32 hostId,
343          afs_int32 errCode, va_list vaList)
344 {
345     int vaEntry;
346     int vaInt;
347     afs_int32 vaLong;
348     char *vaStr;
349     struct AFSFid *vaFid;
350     struct AFSCBFids *vaFids;
351     struct tc_tapeLabel *vaLabel;
352     struct tc_dumpInterface *vaDI;
353     struct tc_dumpArray *vaDA;
354     struct tc_restoreArray *vaRA;
355     struct tciStatusS *vaTCstatus;
356     int num = LogThreadNum();
357     struct in_addr hostAddr;
358     time_t currenttime;
359     char tbuffer[26];
360     struct tm tm;
361     struct audit_msg *msg = calloc(1, sizeof(*msg));
362
363     if (!msg) {
364         return;
365     }
366
367     /* Don't print the timestamp or thread id if we recursed */
368     if (rec == 0) {
369         currenttime = time(0);
370         if (strftime(tbuffer, sizeof(tbuffer), "%a %b %d %H:%M:%S %Y ",
371                      localtime_r(&currenttime, &tm)) !=0)
372             append_msg(msg, tbuffer);
373
374         if (num > -1)
375             append_msg(msg, "[%d] ", num);
376     }
377
378     append_msg(msg, "EVENT %s CODE %d ", audEvent, errCode);
379
380     if (afsName) {
381         hostAddr.s_addr = hostId;
382         append_msg(msg, "NAME %s HOST %s ", afsName, inet_ntoa(hostAddr));
383     }
384
385     vaEntry = va_arg(vaList, int);
386     while (vaEntry != AUD_END) {
387         switch (vaEntry) {
388         case AUD_STR:           /* String */
389             vaStr = (char *)va_arg(vaList, char *);
390             if (vaStr)
391                 append_msg(msg, "STR %s ", vaStr);
392             else
393                 append_msg(msg, "STR <null>");
394             break;
395         case AUD_NAME:          /* Name */
396             vaStr = (char *)va_arg(vaList, char *);
397             if (vaStr)
398                 append_msg(msg, "NAME %s ", vaStr);
399             else
400                 append_msg(msg, "NAME <null>");
401             break;
402         case AUD_ACL:           /* ACL */
403             vaStr = (char *)va_arg(vaList, char *);
404             if (vaStr)
405                 append_msg(msg, "ACL %s ", vaStr);
406             else
407                 append_msg(msg, "ACL <null>");
408             break;
409         case AUD_INT:           /* Integer */
410             vaInt = va_arg(vaList, int);
411             append_msg(msg, "INT %d ", vaInt);
412             break;
413         case AUD_ID:            /* ViceId */
414             vaInt = va_arg(vaList, int);
415             append_msg(msg, "ID %d ", vaInt);
416             break;
417         case AUD_DATE:          /* Date    */
418             vaLong = va_arg(vaList, afs_int32);
419             append_msg(msg, "DATE %u ", vaLong);
420             break;
421         case AUD_HOST:          /* Host ID */
422             vaLong = va_arg(vaList, afs_int32);
423             hostAddr.s_addr = vaLong;
424             append_msg(msg, "HOST %s ", inet_ntoa(hostAddr));
425             break;
426         case AUD_LONG:          /* afs_int32    */
427             vaLong = va_arg(vaList, afs_int32);
428             append_msg(msg, "LONG %d ", vaLong);
429             break;
430         case AUD_FID:           /* AFSFid - contains 3 entries */
431             vaFid = va_arg(vaList, struct AFSFid *);
432             if (vaFid)
433                 append_msg(msg, "FID %u:%u:%u ", vaFid->Volume, vaFid->Vnode,
434                            vaFid->Unique);
435             else
436                 append_msg(msg, "FID %u:%u:%u ", 0, 0, 0);
437             break;
438         case AUD_FIDS:          /* array of Fids */
439             vaFids = va_arg(vaList, struct AFSCBFids *);
440
441             if (vaFids) {
442                 unsigned int i;
443
444                 vaFid = vaFids->AFSCBFids_val;
445
446                 if (vaFid) {
447                     append_msg(msg, "FIDS %u ", vaFids->AFSCBFids_len);
448                     for ( i = 1; i <= vaFids->AFSCBFids_len; i++, vaFid++ )
449                         append_msg(msg, "FID %u:%u:%u ", vaFid->Volume,
450                                    vaFid->Vnode, vaFid->Unique);
451                 } else
452                     append_msg(msg, "FIDS 0 FID 0:0:0 ");
453
454             }
455             break;
456         case AUD_TLBL:          /* butc tape label */
457             vaLabel = va_arg(vaList, struct tc_tapeLabel *);
458
459             if (vaLabel) {
460                 append_msg(msg, "TAPELABEL %d:%.*s:%.*s:%u ",
461                            vaLabel->size,
462                            TC_MAXTAPELEN, vaLabel->afsname,
463                            TC_MAXTAPELEN, vaLabel->pname,
464                            vaLabel->tapeId);
465             } else {
466                 append_msg(msg, "TAPELABEL <null>");
467             }
468             break;
469         case AUD_TDI:
470             vaDI = va_arg(vaList, struct tc_dumpInterface *);
471
472             if (vaDI) {
473                 append_msg(msg,
474     "TCDUMPINTERFACE %.*s:%.*s:%.*s:%d:%d:%d:%d:%.*s:%.*s:%d:%d:%d:%d:%d ",
475     TC_MAXDUMPPATH, vaDI->dumpPath, TC_MAXNAMELEN, vaDI->volumeSetName,
476     TC_MAXNAMELEN, vaDI->dumpName, vaDI->parentDumpId, vaDI->dumpLevel,
477     vaDI->doAppend,
478     vaDI->tapeSet.id, TC_MAXHOSTLEN, vaDI->tapeSet.tapeServer,
479     TC_MAXFORMATLEN, vaDI->tapeSet.format, vaDI->tapeSet.maxTapes,
480     vaDI->tapeSet.a, vaDI->tapeSet.b, vaDI->tapeSet.expDate,
481     vaDI->tapeSet.expType);
482             } else {
483                 append_msg(msg, "TCDUMPINTERFACE <null>");
484             }
485             break;
486         case AUD_TDA:
487             vaDA = va_arg(vaList, struct tc_dumpArray *);
488
489             if (vaDA) {
490                 u_int i;
491                 struct tc_dumpDesc *desc;
492                 struct in_addr hostAddr;
493
494                 desc = vaDA->tc_dumpArray_val;
495                 if (desc) {
496                     append_msg(msg, "DUMPS %d ", vaDA->tc_dumpArray_len);
497                     for (i = 0; i < vaDA->tc_dumpArray_len; i++, desc++) {
498                         hostAddr.s_addr = desc->hostAddr;
499                         append_msg(msg, "DUMP %d:%d:%.*s:%d:%d:%d:%s ",
500                                    desc->vid, desc->vtype, TC_MAXNAMELEN, desc->name,
501                                    desc->partition, desc->date, desc->cloneDate,
502                                    inet_ntoa(hostAddr));
503                     }
504                 } else {
505                     append_msg(msg, "DUMPS 0 DUMP 0:0::0:0:0:0.0.0.0");
506                 }
507             }
508             break;
509         case AUD_TRA:
510             vaRA = va_arg(vaList, struct tc_restoreArray *);
511
512             if (vaRA) {
513                 u_int i;
514                 struct tc_restoreDesc *desc;
515                 struct in_addr hostAddr;
516
517                 desc = vaRA->tc_restoreArray_val;
518                 if (desc) {
519                     append_msg(msg, "RESTORES %d ",
520                                           vaRA->tc_restoreArray_len);
521                     for(i = 0; i < vaRA->tc_restoreArray_len; i++, desc++) {
522                         hostAddr.s_addr = desc->hostAddr;
523                         append_msg(msg,
524                                    "RESTORE %d:%.*s:%d:%d:%d:%d:%d:%d:%d:%s:%.*s:%.*s ",
525                                    desc->flags, TC_MAXTAPELEN, desc->tapeName,
526                                    desc->dbDumpId, desc->initialDumpId,
527                                    desc->position, desc->origVid, desc->vid,
528                                    desc->partition, desc->dumpLevel,
529                                    inet_ntoa(hostAddr), TC_MAXNAMELEN,
530                                    desc->oldName, TC_MAXNAMELEN, desc->newName);
531                     }
532                 } else {
533                     append_msg(msg,
534                         "RESTORES 0 RESTORE 0::0:0:0:0:0:0:0:0.0.0.0::: ");
535                 }
536             }
537             break;
538         case AUD_TSTT:
539             vaTCstatus = va_arg(vaList, struct tciStatusS *);
540
541             if (vaTCstatus)
542                 append_msg(msg, "TCSTATUS %.*s:%d:%d:%d:%d:%.*s:%d:%d ",
543                            TC_MAXNAMELEN, vaTCstatus->taskName,
544                            vaTCstatus->taskId, vaTCstatus->flags,
545                            vaTCstatus->dbDumpId, vaTCstatus->nKBytes,
546                            TC_MAXNAMELEN, vaTCstatus->volumeName,
547                            vaTCstatus->volsFailed,
548                            vaTCstatus->lastPolled);
549             else
550                 append_msg(msg, "TCSTATUS <null>");
551             break;
552         default:
553             append_msg(msg, "--badval-- ");
554             break;
555         }                       /* end switch */
556         vaEntry = va_arg(vaList, int);
557     }                           /* end while */
558
559     MUTEX_ENTER(&audit_lock);
560     multi_send_msg(msg);
561     MUTEX_EXIT(&audit_lock);
562
563     free(msg);
564 }
565
566 #ifdef AFS_PTHREAD_ENV
567 static void
568 osi_audit_init_lock(void)
569 {
570     MUTEX_INIT(&audit_lock, "audit", MUTEX_DEFAULT, 0);
571 }
572 #endif
573
574 void
575 osi_audit_init(void)
576 {
577 #ifdef AFS_PTHREAD_ENV
578     pthread_once(&audit_lock_once, osi_audit_init_lock);
579 #endif /* AFS_PTHREAD_ENV */
580 }
581
582 /* ************************************************************************** */
583 /* The routine that acually does the audit call.
584  * ************************************************************************** */
585 static int
586 osi_audit_internal(char *audEvent,      /* Event name (15 chars or less) */
587                    afs_int32 errCode,   /* The error code */
588                    char *afsName,
589                    afs_int32 hostId,
590                    va_list vaList)
591 {
592 #ifdef AFS_AIX32_ENV
593     afs_int32 code;
594     afs_int32 err;
595     static char BUFFER[32768];
596     int result;
597 #endif
598
599 #ifdef AFS_PTHREAD_ENV
600     /* i'm pretty sure all the server apps now call osi_audit_init(),
601      * but to be extra careful we'll leave this in here for a
602      * while to make sure */
603     pthread_once(&audit_lock_once, osi_audit_init_lock);
604 #endif /* AFS_PTHREAD_ENV */
605
606     if ((osi_audit_all < 0) || (osi_echo_trail < 0))
607         osi_audit_check();
608     if (!osi_audit_all && !auditout_open)
609         return 0;
610
611 #ifdef AFS_AIX32_ENV
612     switch (errCode) {
613     case 0:
614         result = AUDIT_OK;
615         break;
616     case KANOAUTH:              /* kautils.h   */
617     case RXKADNOAUTH:           /* rxkad.h     */
618         result = AUDIT_FAIL_AUTH;
619         break;
620     case EPERM:         /* errno.h     */
621     case EACCES:                /* errno.h     */
622     case PRPERM:                /* pterror.h   */
623         result = AUDIT_FAIL_ACCESS;
624         break;
625     case VL_PERM:               /* vlserver.h  */
626     case BUDB_NOTPERMITTED:     /* budb_errs.h */
627     case BZACCESS:              /* bnode.h     */
628     case VOLSERBAD_ACCESS:      /* volser.h    */
629         result = AUDIT_FAIL_PRIV;
630         break;
631     default:
632         result = AUDIT_FAIL;
633         break;
634     }
635 #endif
636
637 #ifdef AFS_AIX32_ENV
638     MUTEX_ENTER(&audit_lock);
639     bufferPtr = BUFFER;
640
641     /* Put the error code into the buffer list */
642     *(int *)bufferPtr = errCode;
643     bufferPtr += sizeof(errCode);
644
645     audmakebuf(audEvent, vaList);
646
647     bufferLen = (int)((afs_int32) bufferPtr - (afs_int32) & BUFFER[0]);
648     code = auditlog(audEvent, result, BUFFER, bufferLen);
649     MUTEX_EXIT(&audit_lock);
650 #else
651     if (auditout_open) {
652         printbuf(0, audEvent, afsName, hostId, errCode, vaList);
653     }
654 #endif
655
656     return 0;
657 }
658 int
659 osi_audit(char *audEvent,       /* Event name (15 chars or less) */
660           afs_int32 errCode,    /* The error code */
661           ...)
662 {
663     va_list vaList;
664
665     if ((osi_audit_all < 0) || (osi_echo_trail < 0))
666         osi_audit_check();
667     if (!osi_audit_all && !auditout_open)
668         return 0;
669
670     va_start(vaList, errCode);
671     osi_audit_internal(audEvent, errCode, NULL, 0, vaList);
672     va_end(vaList);
673
674     return 0;
675 }
676
677 /* ************************************************************************** */
678 /* Given a RPC call structure, this routine extracts the name and host id from the
679  * call and includes it within the audit information.
680  * ************************************************************************** */
681 int
682 osi_auditU(struct rx_call *call, char *audEvent, int errCode, ...)
683 {
684     struct rx_connection *conn;
685     struct rx_peer *peer;
686     afs_int32 secClass;
687     afs_int32 code;
688     char afsName[MAXKTCNAMELEN + MAXKTCNAMELEN + MAXKTCREALMLEN + 3];
689     afs_int32 hostId;
690     va_list vaList;
691
692     if (osi_audit_all < 0)
693         osi_audit_check();
694     if (!osi_audit_all && !auditout_open)
695         return 0;
696
697     strcpy(afsName, "--Unknown--");
698     hostId = 0;
699
700     if (call) {
701         conn = rx_ConnectionOf(call);   /* call -> conn) */
702         if (conn) {
703             secClass = rx_SecurityClassOf(conn);        /* conn -> securityIndex */
704             if (secClass == RX_SECIDX_NULL) {   /* unauthenticated */
705                 osi_audit("AFS_Aud_Unauth", (-1), AUD_STR, audEvent, AUD_END);
706                 strcpy(afsName, "--UnAuth--");
707             } else if (secClass == RX_SECIDX_KAD || secClass == RX_SECIDX_KAE) {
708                 /* authenticated with rxkad */
709                 char tcell[MAXKTCREALMLEN];
710                 char name[MAXKTCNAMELEN];
711                 char inst[MAXKTCNAMELEN];
712
713                 code =
714                     rxkad_GetServerInfo(conn, NULL, NULL, name, inst, tcell,
715                                         NULL);
716                 if (code) {
717                     osi_audit("AFS_Aud_NoAFSId", (-1), AUD_STR, audEvent, AUD_END);
718                     strcpy(afsName, "--NoName--");
719                 } else {
720                     afs_int32 islocal = 0;
721                     if (audit_user_check.islocal) {
722                         islocal =
723                             audit_user_check.islocal(audit_user_check.rock,
724                                                      name, inst, tcell);
725                     }
726                     strlcpy(afsName, name, sizeof(afsName));
727                     if (inst[0]) {
728                         strlcat(afsName, ".", sizeof(afsName));
729                         strlcat(afsName, inst, sizeof(afsName));
730                     }
731                     if (tcell[0] && !islocal) {
732                         strlcat(afsName, "@", sizeof(afsName));
733                         strlcat(afsName, tcell, sizeof(afsName));
734                     }
735                 }
736             } else {            /* Unauthenticated and/or unknown */
737                 osi_audit("AFS_Aud_UnknSec", (-1), AUD_STR, audEvent, AUD_END);
738                 strcpy(afsName, "--Unknown--");
739             }
740             peer = rx_PeerOf(conn);     /* conn -> peer */
741             if (peer)
742                 hostId = rx_HostOf(peer);       /* peer -> host */
743             else
744                 osi_audit("AFS_Aud_NoHost", (-1), AUD_STR, audEvent, AUD_END);
745         } else {                /* null conn */
746             osi_audit("AFS_Aud_NoConn", (-1), AUD_STR, audEvent, AUD_END);
747         }
748     } else {                    /* null call */
749         osi_audit("AFS_Aud_NoCall", (-1), AUD_STR, audEvent, AUD_END);
750     }
751     va_start(vaList, errCode);
752     osi_audit_internal(audEvent, errCode, afsName, hostId, vaList);
753     va_end(vaList);
754     return 0;
755 }
756
757 /* ************************************************************************** */
758 /* Determines whether auditing is on or off by looking at the Audit file.
759  * If the string AFS_AUDIT_AllEvents is defined in the file, then auditing will be
760  * enabled.
761  * ************************************************************************** */
762
763 int
764 osi_audit_check(void)
765 {
766     FILE *fds;
767     int onoff;
768     char event[257];
769
770     osi_audit_all = 1;          /* say we made check (>= 0) */
771     /* and assume audit all events (for now) */
772     onoff = 0;                  /* assume we will turn auditing off */
773     osi_echo_trail = 0;         /* assume no echoing   */
774
775     fds = fopen(AFSDIR_SERVER_AUDIT_FILEPATH, "r");
776     if (fds) {
777         while (fscanf(fds, "%256s", event) > 0) {
778             if (strcmp(event, "AFS_AUDIT_AllEvents") == 0)
779                 onoff = 1;
780
781             if (strcmp(event, "Echo_Trail") == 0)
782                 osi_echo_trail = 1;
783         }
784         fclose(fds);
785     }
786
787     /* Audit this event all of the time */
788     if (onoff)
789         osi_audit("AFS_Aud_On", 0, AUD_END);
790     else
791         osi_audit("AFS_Aud_Off", 0, AUD_END);
792
793     /* Now set whether we audit all events from here on out */
794     osi_audit_all = onoff;
795
796     return 0;
797 }
798
799 /*
800  * Handle parsing a string: [interface_name:]filespec[:options]
801  * The string a:b will parse 'a' as the interface name and 'b' as the filespec.
802  * Note that the string pointed by optionstr will be modified
803  * by strtok_r by inserting '\0' between the tokens.
804  * The values returned in interface_name, filespec and options
805  * are pointers to the 'sub-strings' within optionstr.
806  */
807 static int
808 parse_file_options(char *optionstr,
809                    const char **interface_name,
810                    char **filespec,
811                    char **options)
812 {
813     int code = 0;
814     char *opt_cursor = optionstr;
815     char *tok1 = NULL, *tok2 = NULL, *tok3 = NULL, *tokptrsave = NULL;
816
817     /*
818      * Handle the fact that strtok doesn't handle empty fields e.g. a::b
819      * and will return tok1-> a tok2-> b
820      */
821
822     /* 1st field */
823     if (*opt_cursor != ':') {
824         tok1 = strtok_r(opt_cursor, ":", &tokptrsave);
825         opt_cursor = strtok_r(NULL, "", &tokptrsave);
826     } else  {
827         /* Skip the ':' */
828         opt_cursor++;
829     }
830
831     /* 2nd field */
832     if (opt_cursor != NULL) {
833         if (*opt_cursor != ':') {
834             tok2 = strtok_r(opt_cursor, ":", &tokptrsave);
835             opt_cursor = strtok_r(NULL, "", &tokptrsave);
836         } else {
837             /* Skip the ':' */
838             opt_cursor++;
839         }
840     }
841
842     /* 3rd field is just the remainder if any */
843     tok3 = opt_cursor;
844
845     if (tok1 == NULL || strlen(tok1) == 0) {
846         fprintf(stderr, "Missing -auditlog parameter\n");
847         code = EINVAL;
848         goto done;
849     }
850
851     /* If only one token, then it's the filespec */
852     if (tok2 == NULL && tok3 == NULL) {
853         *filespec = tok1;
854     } else {
855         *interface_name = tok1;
856         *filespec = tok2;
857         *options = tok3;
858     }
859
860  done:
861     return code;
862 }
863
864 /*
865  * Parse the options looking for comma-seperated values.
866  */
867 static int
868 parse_option_string(const struct osi_audit_ops *ops, void *rock, char *options)
869 {
870     int code = 0;
871     char *tok1, *tokptrsave = NULL;
872
873     tok1 = strtok_r(options, ",", &tokptrsave);
874     while (tok1) {
875         /* Handle opt=val or just opt */
876         char *opt, *val;
877         char *optvalsave;
878
879         opt = strtok_r(tok1, "=", &optvalsave);
880         val = strtok_r(NULL, "", &optvalsave);
881
882         code = ops->set_option(rock, opt, val);
883
884         if (code) {
885             /* Bad option */
886             goto done;
887         }
888         tok1 = strtok_r(NULL, ",", &tokptrsave);
889     }
890
891  done:
892     return code;
893 }
894
895 /*
896  * Process -auditlog
897  *  [interface]:filespec[:options]
898  *      interface - interface name (optional - defaults to default_interface)
899  *      filespec - depends on the interface (required)
900  *      options - optional string passed to interface
901  * Returns 0 - success
902  *        EINVAL - option error
903  *        ENOMEM - error allocating memory
904  */
905
906 int
907 osi_audit_file(const char *fileplusoptions)
908 {
909
910     int idx;
911     int code;
912
913     char *optionstr = NULL;
914
915     const char *interface_name = NULL;
916     char *filespec = NULL;
917     char *options = NULL;
918
919     const struct osi_audit_ops *ops = NULL;
920     struct audit_log *new_alog = NULL;
921
922     /* Use the default unless specified */
923     interface_name = audit_interfaces[default_interface].name;
924
925     /* dup of the input string so the parsing can safely modify it */
926     optionstr = strdup(fileplusoptions);
927     if (!optionstr) {
928         code = ENOMEM;
929         goto done;
930     }
931
932     code = parse_file_options(optionstr, &interface_name, &filespec, &options);
933     if (code)
934         goto done;
935
936     if (interface_name && strlen(interface_name) != 0) {
937         for (idx = 0; idx < N_INTERFACES; idx++) {
938             if (strcmp(interface_name, audit_interfaces[idx].name) == 0) {
939                 ops = audit_interfaces[idx].ops;
940                 break;
941             }
942         }
943         if (ops == NULL) {
944             /* Couldn't find the interface name */
945             fprintf(stderr, "Could not find the specified audit interface %s\n", interface_name);
946             code = EINVAL;
947             goto done;
948         }
949     } else {
950         fprintf(stderr, "Missing interface name\n");
951         code = EINVAL;
952         goto done;
953     }
954
955     if (filespec == NULL || strlen(filespec) == 0) {
956         fprintf(stderr, "Missing auditlog path for %s interface\n", interface_name);
957         code = EINVAL;
958         goto done;
959     }
960
961     opr_Assert(ops->create_interface != NULL);
962     opr_Assert(ops->close_interface != NULL);
963     opr_Assert(ops->open_file != NULL);
964     opr_Assert(ops->send_msg != NULL);
965     opr_Assert(ops->print_interface_stats != NULL);
966     /* open_interface, set_option and close_interface are optional */
967
968     new_alog = calloc(1, sizeof(*new_alog));
969     if (!new_alog) {
970         code = ENOMEM;
971         goto done;
972     }
973
974     new_alog->audit_ops = ops;
975     new_alog->auditout_open = 0;
976
977     new_alog->context = ops->create_interface();
978     if (new_alog->context == NULL) {
979             code = ENOMEM;
980             goto done;
981     }
982
983     if (options != NULL && ops->set_option != NULL) {
984         /* Split the option string at commas */
985         code = parse_option_string(ops, new_alog->context, options);
986         if (code)
987             goto done;
988     }
989
990     code = ops->open_file(new_alog->context, filespec);
991     if (code) {
992         /* Error opening file */
993         goto done;
994     }
995
996     new_alog->auditout_open = 1;
997     auditout_open = 1;
998
999     /* Add to chain of active interfaces */
1000     opr_queue_Append(&audit_logs, &new_alog->link);
1001     new_alog = NULL;
1002
1003     code = 0;
1004
1005  done:
1006      if (code) {
1007          /* Error condition present.. */
1008          if (new_alog) {
1009              ops->close_interface(&new_alog->context);
1010              free(new_alog);
1011          }
1012      }
1013
1014     if (optionstr)
1015         free(optionstr);
1016     return code;
1017 }
1018
1019 /*
1020  * Set the default interface
1021  * return 0 for success
1022  *        EINVAL missing or invalid interface name
1023  */
1024 int
1025 osi_audit_interface(const char *interface)
1026 {
1027     int idx;
1028
1029     if (interface == NULL || strlen(interface) == 0)
1030         return EINVAL;
1031
1032     for (idx = 0; idx < N_INTERFACES; idx++) {
1033         if (strcmp(interface, audit_interfaces[idx].name) == 0) {
1034             default_interface = idx;
1035             return 0;
1036         }
1037     }
1038     return EINVAL;
1039 }
1040
1041 /*
1042  * Let the interfaces finish initialization
1043  */
1044 void
1045 osi_audit_open(void)
1046 {
1047     struct opr_queue *cursor;
1048
1049     for (opr_queue_Scan(&audit_logs, cursor)) {
1050         struct audit_log *alog;
1051         alog = opr_queue_Entry(cursor, struct audit_log, link);
1052         if (alog->auditout_open && alog->audit_ops->open_interface != NULL)
1053             alog->audit_ops->open_interface(alog->context);
1054     }
1055 }
1056
1057 /*
1058  * Shutdown the interfaces
1059  */
1060 void
1061 osi_audit_close(void)
1062 {
1063     struct opr_queue *cursor, *cursorsave;
1064
1065     for (opr_queue_ScanSafe(&audit_logs, cursor, cursorsave)) {
1066         struct audit_log *alog;
1067         alog = opr_queue_Entry(cursor, struct audit_log, link);
1068         alog->audit_ops->close_interface(&alog->context);
1069         opr_queue_Remove(&alog->link);
1070         free(alog);
1071     }
1072 }
1073
1074 void
1075 osi_audit_set_user_check(void *rock,
1076                          int (*islocal) (void *rock, char *name, char *inst,
1077                                          char *cell))
1078 {
1079     audit_user_check.rock = rock;
1080     audit_user_check.islocal = islocal;
1081 }
1082
1083 void
1084 audit_PrintStats(FILE *out)
1085 {
1086     struct opr_queue *cursor;
1087
1088     for (opr_queue_Scan(&audit_logs, cursor)) {
1089         struct audit_log *alog;
1090         alog = opr_queue_Entry(cursor, struct audit_log, link);
1091         if (alog->auditout_open)
1092             alog->audit_ops->print_interface_stats(alog->context, out);
1093     }
1094 }