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