audit: Add cmd helper for processing audit options
[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  * Helper for processing cmd line options
801  *
802  * Process the parameters for the -audit-interface and -auditlog command line
803  * options
804  *
805  * @param[in] default_iface
806  *      String for the default audit interface or NULL
807  * @param[in] audit_loglist
808  *      Pointer to a cmd_item list of audit logs or NULL
809  *
810  * @return status
811  * @retval      0       - success
812  * @retval      -1      - Error (message printed to stderr)
813  */
814
815 int
816 osi_audit_cmd_Options(char *default_iface, struct cmd_item *audit_loglist)
817 {
818
819     if (default_iface) {
820         if (osi_audit_interface(default_iface)) {
821             fprintf(stderr, "Invalid auditinterface '%s'\n", default_iface);
822             return -1;
823         }
824     }
825
826     while (audit_loglist != NULL) {
827         if (osi_audit_file(audit_loglist->data)) {
828             fprintf(stderr, "Error processing auditlog options '%s'\n",
829                     audit_loglist->data);
830             return -1;
831         }
832         audit_loglist = audit_loglist->next;
833     }
834     return 0;
835 }
836
837  /*
838  * Handle parsing a string: [interface_name:]filespec[:options]
839  * The string a:b will parse 'a' as the interface name and 'b' as the filespec.
840  * Note that the string pointed by optionstr will be modified
841  * by strtok_r by inserting '\0' between the tokens.
842  * The values returned in interface_name, filespec and options
843  * are pointers to the 'sub-strings' within optionstr.
844  */
845 static int
846 parse_file_options(char *optionstr,
847                    const char **interface_name,
848                    char **filespec,
849                    char **options)
850 {
851     int code = 0;
852     char *opt_cursor = optionstr;
853     char *tok1 = NULL, *tok2 = NULL, *tok3 = NULL, *tokptrsave = NULL;
854
855     /*
856      * Handle the fact that strtok doesn't handle empty fields e.g. a::b
857      * and will return tok1-> a tok2-> b
858      */
859
860     /* 1st field */
861     if (*opt_cursor != ':') {
862         tok1 = strtok_r(opt_cursor, ":", &tokptrsave);
863         opt_cursor = strtok_r(NULL, "", &tokptrsave);
864     } else  {
865         /* Skip the ':' */
866         opt_cursor++;
867     }
868
869     /* 2nd field */
870     if (opt_cursor != NULL) {
871         if (*opt_cursor != ':') {
872             tok2 = strtok_r(opt_cursor, ":", &tokptrsave);
873             opt_cursor = strtok_r(NULL, "", &tokptrsave);
874         } else {
875             /* Skip the ':' */
876             opt_cursor++;
877         }
878     }
879
880     /* 3rd field is just the remainder if any */
881     tok3 = opt_cursor;
882
883     if (tok1 == NULL || strlen(tok1) == 0) {
884         fprintf(stderr, "Missing -auditlog parameter\n");
885         code = EINVAL;
886         goto done;
887     }
888
889     /* If only one token, then it's the filespec */
890     if (tok2 == NULL && tok3 == NULL) {
891         *filespec = tok1;
892     } else {
893         *interface_name = tok1;
894         *filespec = tok2;
895         *options = tok3;
896     }
897
898  done:
899     return code;
900 }
901
902 /*
903  * Parse the options looking for comma-seperated values.
904  */
905 static int
906 parse_option_string(const struct osi_audit_ops *ops, void *rock, char *options)
907 {
908     int code = 0;
909     char *tok1, *tokptrsave = NULL;
910
911     tok1 = strtok_r(options, ",", &tokptrsave);
912     while (tok1) {
913         /* Handle opt=val or just opt */
914         char *opt, *val;
915         char *optvalsave;
916
917         opt = strtok_r(tok1, "=", &optvalsave);
918         val = strtok_r(NULL, "", &optvalsave);
919
920         code = ops->set_option(rock, opt, val);
921
922         if (code) {
923             /* Bad option */
924             goto done;
925         }
926         tok1 = strtok_r(NULL, ",", &tokptrsave);
927     }
928
929  done:
930     return code;
931 }
932
933 /*
934  * Process -auditlog
935  *  [interface]:filespec[:options]
936  *      interface - interface name (optional - defaults to default_interface)
937  *      filespec - depends on the interface (required)
938  *      options - optional string passed to interface
939  * Returns 0 - success
940  *        EINVAL - option error
941  *        ENOMEM - error allocating memory
942  */
943
944 int
945 osi_audit_file(const char *fileplusoptions)
946 {
947
948     int idx;
949     int code;
950
951     char *optionstr = NULL;
952
953     const char *interface_name = NULL;
954     char *filespec = NULL;
955     char *options = NULL;
956
957     const struct osi_audit_ops *ops = NULL;
958     struct audit_log *new_alog = NULL;
959
960     /* Use the default unless specified */
961     interface_name = audit_interfaces[default_interface].name;
962
963     /* dup of the input string so the parsing can safely modify it */
964     optionstr = strdup(fileplusoptions);
965     if (!optionstr) {
966         code = ENOMEM;
967         goto done;
968     }
969
970     code = parse_file_options(optionstr, &interface_name, &filespec, &options);
971     if (code)
972         goto done;
973
974     if (interface_name && strlen(interface_name) != 0) {
975         for (idx = 0; idx < N_INTERFACES; idx++) {
976             if (strcmp(interface_name, audit_interfaces[idx].name) == 0) {
977                 ops = audit_interfaces[idx].ops;
978                 break;
979             }
980         }
981         if (ops == NULL) {
982             /* Couldn't find the interface name */
983             fprintf(stderr, "Could not find the specified audit interface %s\n", interface_name);
984             code = EINVAL;
985             goto done;
986         }
987     } else {
988         fprintf(stderr, "Missing interface name\n");
989         code = EINVAL;
990         goto done;
991     }
992
993     if (filespec == NULL || strlen(filespec) == 0) {
994         fprintf(stderr, "Missing auditlog path for %s interface\n", interface_name);
995         code = EINVAL;
996         goto done;
997     }
998
999     opr_Assert(ops->create_interface != NULL);
1000     opr_Assert(ops->close_interface != NULL);
1001     opr_Assert(ops->open_file != NULL);
1002     opr_Assert(ops->send_msg != NULL);
1003     opr_Assert(ops->print_interface_stats != NULL);
1004     /* open_interface, set_option and close_interface are optional */
1005
1006     new_alog = calloc(1, sizeof(*new_alog));
1007     if (!new_alog) {
1008         code = ENOMEM;
1009         goto done;
1010     }
1011
1012     new_alog->audit_ops = ops;
1013     new_alog->auditout_open = 0;
1014
1015     new_alog->context = ops->create_interface();
1016     if (new_alog->context == NULL) {
1017             code = ENOMEM;
1018             goto done;
1019     }
1020
1021     if (options != NULL && ops->set_option != NULL) {
1022         /* Split the option string at commas */
1023         code = parse_option_string(ops, new_alog->context, options);
1024         if (code)
1025             goto done;
1026     }
1027
1028     code = ops->open_file(new_alog->context, filespec);
1029     if (code) {
1030         /* Error opening file */
1031         goto done;
1032     }
1033
1034     new_alog->auditout_open = 1;
1035     auditout_open = 1;
1036
1037     /* Add to chain of active interfaces */
1038     opr_queue_Append(&audit_logs, &new_alog->link);
1039     new_alog = NULL;
1040
1041     code = 0;
1042
1043  done:
1044      if (code) {
1045          /* Error condition present.. */
1046          if (new_alog) {
1047              ops->close_interface(&new_alog->context);
1048              free(new_alog);
1049          }
1050      }
1051
1052     if (optionstr)
1053         free(optionstr);
1054     return code;
1055 }
1056
1057 /*
1058  * Set the default interface
1059  * return 0 for success
1060  *        EINVAL missing or invalid interface name
1061  */
1062 int
1063 osi_audit_interface(const char *interface)
1064 {
1065     int idx;
1066
1067     if (interface == NULL || strlen(interface) == 0)
1068         return EINVAL;
1069
1070     for (idx = 0; idx < N_INTERFACES; idx++) {
1071         if (strcmp(interface, audit_interfaces[idx].name) == 0) {
1072             default_interface = idx;
1073             return 0;
1074         }
1075     }
1076     return EINVAL;
1077 }
1078
1079 /*
1080  * Let the interfaces finish initialization
1081  */
1082 void
1083 osi_audit_open(void)
1084 {
1085     struct opr_queue *cursor;
1086
1087     for (opr_queue_Scan(&audit_logs, cursor)) {
1088         struct audit_log *alog;
1089         alog = opr_queue_Entry(cursor, struct audit_log, link);
1090         if (alog->auditout_open && alog->audit_ops->open_interface != NULL)
1091             alog->audit_ops->open_interface(alog->context);
1092     }
1093 }
1094
1095 /*
1096  * Shutdown the interfaces
1097  */
1098 void
1099 osi_audit_close(void)
1100 {
1101     struct opr_queue *cursor, *cursorsave;
1102
1103     for (opr_queue_ScanSafe(&audit_logs, cursor, cursorsave)) {
1104         struct audit_log *alog;
1105         alog = opr_queue_Entry(cursor, struct audit_log, link);
1106         alog->audit_ops->close_interface(&alog->context);
1107         opr_queue_Remove(&alog->link);
1108         free(alog);
1109     }
1110 }
1111
1112 void
1113 osi_audit_set_user_check(void *rock,
1114                          int (*islocal) (void *rock, char *name, char *inst,
1115                                          char *cell))
1116 {
1117     audit_user_check.rock = rock;
1118     audit_user_check.islocal = islocal;
1119 }
1120
1121 void
1122 audit_PrintStats(FILE *out)
1123 {
1124     struct opr_queue *cursor;
1125
1126     for (opr_queue_Scan(&audit_logs, cursor)) {
1127         struct audit_log *alog;
1128         alog = opr_queue_Entry(cursor, struct audit_log, link);
1129         if (alog->auditout_open)
1130             alog->audit_ops->print_interface_stats(alog->context, out);
1131     }
1132 }