Coverage Report

Created: 2026-09-03 07:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/third_party/heimdal/lib/base/log.c
Line
Count
Source
1
/*
2
 * Copyright (c) 1997-2020 Kungliga Tekniska Högskolan
3
 * (Royal Institute of Technology, Stockholm, Sweden).
4
 * All rights reserved.
5
 *
6
 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 *
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 *
15
 * 2. Redistributions in binary form must reproduce the above copyright
16
 *    notice, this list of conditions and the following disclaimer in the
17
 *    documentation and/or other materials provided with the distribution.
18
 *
19
 * 3. Neither the name of the Institute nor the names of its contributors
20
 *    may be used to endorse or promote products derived from this software
21
 *    without specific prior written permission.
22
 *
23
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33
 * SUCH DAMAGE.
34
 */
35
36
#include "baselocl.h"
37
#include "heim_threads.h"
38
#include "heimbase-atomics.h"
39
#include "heimbase.h"
40
#include "heimbase-svc.h"
41
#include <assert.h>
42
#include <stdarg.h>
43
#include <vis.h>
44
#include <base64.h>
45
46
struct heim_log_facility_internal {
47
    int min;
48
    int max;
49
    heim_log_log_func_t log_func;
50
    heim_log_close_func_t close_func;
51
    void *data;
52
};
53
54
struct heim_log_facility_s {
55
    char *program;
56
    heim_base_atomic(uint32_t) refs;
57
    size_t len;
58
    struct heim_log_facility_internal *val;
59
};
60
61
typedef struct heim_pcontext_s *heim_pcontext;
62
typedef struct heim_pconfig *heim_pconfig;
63
struct heim_svc_req_desc_common_s {
64
    HEIM_SVC_REQUEST_DESC_COMMON_ELEMENTS;
65
};
66
67
static struct heim_log_facility_internal *
68
log_realloc(heim_log_facility *f)
69
0
{
70
0
    struct heim_log_facility_internal *fp;
71
0
    fp = realloc(f->val, (f->len + 1) * sizeof(*f->val));
72
0
    if (fp == NULL)
73
0
        return NULL;
74
0
    f->len++;
75
0
    f->val = fp;
76
0
    fp += f->len - 1;
77
0
    return fp;
78
0
}
79
80
struct s2i {
81
    const char *s;
82
    int val;
83
};
84
85
#define L(X) { #X, LOG_ ## X }
86
87
static struct s2i syslogvals[] = {
88
    L(EMERG),
89
    L(ALERT),
90
    L(CRIT),
91
    L(ERR),
92
    L(WARNING),
93
    L(NOTICE),
94
    L(INFO),
95
    L(DEBUG),
96
97
    L(AUTH),
98
#ifdef LOG_AUTHPRIV
99
    L(AUTHPRIV),
100
#endif
101
#ifdef LOG_CRON
102
    L(CRON),
103
#endif
104
    L(DAEMON),
105
#ifdef LOG_FTP
106
    L(FTP),
107
#endif
108
    L(KERN),
109
    L(LPR),
110
    L(MAIL),
111
#ifdef LOG_NEWS
112
    L(NEWS),
113
#endif
114
    L(SYSLOG),
115
    L(USER),
116
#ifdef LOG_UUCP
117
    L(UUCP),
118
#endif
119
    L(LOCAL0),
120
    L(LOCAL1),
121
    L(LOCAL2),
122
    L(LOCAL3),
123
    L(LOCAL4),
124
    L(LOCAL5),
125
    L(LOCAL6),
126
    L(LOCAL7),
127
    { NULL, -1 }
128
};
129
130
static int
131
find_value(const char *s, struct s2i *table)
132
0
{
133
0
    while (table->s && strcasecmp(table->s, s) != 0)
134
0
        table++;
135
0
    return table->val;
136
0
}
137
138
heim_error_code
139
heim_initlog(heim_context context,
140
             const char *program,
141
             heim_log_facility **fac)
142
0
{
143
0
    heim_log_facility *f = calloc(1, sizeof(*f));
144
0
    if (f == NULL)
145
0
        return heim_enomem(context);
146
0
    f->refs = 1;
147
0
    f->program = strdup(program);
148
0
    if (f->program == NULL) {
149
0
        free(f);
150
0
        return heim_enomem(context);
151
0
    }
152
0
    *fac = f;
153
0
    return 0;
154
0
}
155
156
heim_log_facility *
157
heim_log_ref(heim_log_facility *fac)
158
0
{
159
0
    if (fac)
160
0
        (void) heim_base_atomic_inc_32(&fac->refs);
161
0
    return fac;
162
0
}
163
164
heim_error_code
165
heim_addlog_func(heim_context context,
166
                 heim_log_facility *fac,
167
                 int min,
168
                 int max,
169
                 heim_log_log_func_t log_func,
170
                 heim_log_close_func_t close_func,
171
                 void *data)
172
0
{
173
0
    struct heim_log_facility_internal *fp = log_realloc(fac);
174
0
    if (fp == NULL)
175
0
        return heim_enomem(context);
176
0
    fp->min = min;
177
0
    fp->max = max;
178
0
    fp->log_func = log_func;
179
0
    fp->close_func = close_func;
180
0
    fp->data = data;
181
0
    return 0;
182
0
}
183
184
185
struct _heimdal_syslog_data{
186
    int priority;
187
};
188
189
static void HEIM_CALLCONV
190
log_syslog(heim_context context, const char *timestr,
191
           const char *msg, void *data)
192
0
{
193
0
    struct _heimdal_syslog_data *s = data;
194
0
    syslog(s->priority, "%s", msg);
195
0
}
196
197
static void HEIM_CALLCONV
198
close_syslog(void *data)
199
0
{
200
0
    free(data);
201
0
    closelog();
202
0
}
203
204
static heim_error_code
205
open_syslog(heim_context context,
206
            heim_log_facility *facility, int min, int max,
207
            const char *sev, const char *fac)
208
0
{
209
0
    struct _heimdal_syslog_data *sd;
210
0
    heim_error_code ret;
211
0
    int i;
212
213
0
    if (facility == NULL)
214
0
        return EINVAL;
215
0
    if ((sd = calloc(1, sizeof(*sd))) == NULL)
216
0
        return heim_enomem(context);
217
0
    i = find_value(sev, syslogvals);
218
0
    if (i == -1)
219
0
        i = LOG_ERR;
220
0
    sd->priority = i;
221
0
    i = find_value(fac, syslogvals);
222
0
    if (i == -1)
223
0
        i = LOG_AUTH;
224
0
    sd->priority |= i;
225
0
    roken_openlog(facility->program, LOG_PID | LOG_NDELAY, i);
226
0
    ret = heim_addlog_func(context, facility, min, max, log_syslog,
227
0
                           close_syslog, sd);
228
0
    if (ret)
229
0
        free(sd);
230
0
    else
231
0
        sd = NULL;
232
0
    return ret;
233
0
}
234
235
struct file_data {
236
    char *filename;
237
    const char *mode;
238
    struct timeval tv;
239
    FILE *fd;
240
    int disp;
241
0
#define FILEDISP_KEEPOPEN       0x1
242
0
#define FILEDISP_REOPEN         0x2
243
0
#define FILEDISP_IFEXISTS       0x4
244
};
245
246
#ifndef O_CLOEXEC
247
#define O_CLOEXEC 0
248
#endif
249
250
static void HEIM_CALLCONV
251
log_file(heim_context context, const char *timestr, const char *msg, void *data)
252
0
{
253
0
    struct timeval tv;
254
0
    struct file_data *f = data;
255
0
    FILE *logf = f->fd;
256
0
    char *msgclean;
257
0
    size_t i = 0;
258
0
    size_t j;
259
260
0
    if (f->filename && (logf == NULL || (f->disp & FILEDISP_REOPEN))) {
261
0
        int flags = O_WRONLY|O_APPEND;
262
0
        int fd;
263
264
0
        if (f->mode[0] == 'e') {
265
0
            flags |= O_CLOEXEC;
266
0
            i = 1;
267
0
        }
268
0
        if (f->mode[i] == 'w')
269
0
            flags |= O_TRUNC;
270
0
        if (f->mode[i + 1] == '+')
271
0
            flags |= O_RDWR;
272
273
0
        if (f->disp & FILEDISP_IFEXISTS) {
274
            /* Cache failure for 1s */
275
0
            gettimeofday(&tv, NULL);
276
0
            if (tv.tv_sec == f->tv.tv_sec)
277
0
                return;
278
0
        } else {
279
0
            flags |= O_CREAT;
280
0
        }
281
282
0
        fd = open(f->filename, flags, 0666); /* umask best be set */
283
0
        if (fd == -1) {
284
0
            if (f->disp & FILEDISP_IFEXISTS)
285
0
                gettimeofday(&f->tv, NULL);
286
0
            return;
287
0
        }
288
0
        rk_cloexec(fd);
289
0
        logf = fdopen(fd, f->mode);
290
0
    }
291
0
    if (f->fd == NULL && (f->disp & FILEDISP_KEEPOPEN))
292
0
        f->fd = logf;
293
0
    if (logf == NULL)
294
0
        return;
295
    /*
296
     * make sure the log doesn't contain special chars:
297
     * we used to use strvisx(3) to encode the log, but this is
298
     * inconsistent with our syslog(3) code which does not do this.
299
     * It also makes it inelegant to write data which has already
300
     * been quoted such as what krb5_unparse_principal() gives us.
301
     * So, we change here to eat the special characters, instead.
302
     */
303
0
    if (msg && (msgclean = strdup(msg))) {
304
0
        for (i = 0, j = 0; msg[i]; i++)
305
0
            if (msg[i] >= 32 || msg[i] == '\t')
306
0
                msgclean[j++] = msg[i];
307
0
        fprintf(logf, "%s %s\n", timestr ? timestr : "", msgclean);
308
0
        free(msgclean);
309
0
    }
310
0
    if (logf != f->fd)
311
0
        fclose(logf);
312
0
}
313
314
static void HEIM_CALLCONV
315
close_file(void *data)
316
0
{
317
0
    struct file_data *f = data;
318
0
    if (f->fd && f->fd != stdout && f->fd != stderr)
319
0
        fclose(f->fd);
320
0
    free(f->filename);
321
0
    free(data);
322
0
}
323
324
static heim_error_code
325
open_file(heim_context context, heim_log_facility *fac, int min, int max,
326
          const char *filename, const char *mode, FILE *f, int disp,
327
          int exp_tokens)
328
0
{
329
0
    heim_error_code ret = 0;
330
0
    struct file_data *fd;
331
332
0
    if ((fd = calloc(1, sizeof(*fd))) == NULL)
333
0
        return heim_enomem(context);
334
335
0
    fd->filename = NULL;
336
0
    fd->mode = mode;
337
0
    fd->fd = f;
338
0
    fd->disp = disp;
339
340
0
    if (filename) {
341
0
        if (exp_tokens)
342
0
            ret = heim_expand_path_tokens(context, filename, 1, &fd->filename, NULL);
343
0
        else if ((fd->filename = strdup(filename)) == NULL)
344
0
            ret = heim_enomem(context);
345
0
    }
346
0
    if (ret == 0)
347
0
        ret = heim_addlog_func(context, fac, min, max, log_file, close_file, fd);
348
0
    if (ret) {
349
0
        free(fd->filename);
350
0
        free(fd);
351
0
    } else if (disp & FILEDISP_KEEPOPEN) {
352
0
        log_file(context, NULL, NULL, fd);
353
0
        fd = NULL;
354
0
    }
355
0
    return ret;
356
0
}
357
358
heim_error_code
359
heim_addlog_dest(heim_context context, heim_log_facility *f, const char *orig)
360
0
{
361
0
    heim_error_code ret = 0;
362
0
    int min = 0, max = 3, n;
363
0
    char c;
364
0
    const char *p = orig;
365
#ifdef _WIN32
366
    const char *q;
367
#endif
368
369
0
    n = sscanf(p, "%d%c%d/", &min, &c, &max);
370
0
    if (n == 2) {
371
0
        if (ISPATHSEP(c)) {
372
0
            if (min < 0) {
373
0
                max = -min;
374
0
                min = 0;
375
0
            } else {
376
0
                max = min;
377
0
            }
378
0
        }
379
0
        if (c == '-')
380
0
            max = -1;
381
0
    }
382
0
    if (n) {
383
#ifdef _WIN32
384
        q = strrchr(p, '\\');
385
        if (q != NULL)
386
            p = q;
387
        else
388
#endif
389
0
            p = strchr(p, '/');
390
0
        if (p == NULL) {
391
0
            heim_set_error_message(context, EINVAL /*XXX HEIM_ERR_LOG_PARSE*/,
392
0
                                   N_("failed to parse \"%s\"", ""), orig);
393
0
            return EINVAL /*XXX HEIM_ERR_LOG_PARSE*/;
394
0
        }
395
0
        p++;
396
0
    }
397
0
    if (strcmp(p, "STDERR") == 0) {
398
0
        ret = open_file(context, f, min, max, NULL, "a", stderr,
399
0
                        FILEDISP_KEEPOPEN, 0);
400
0
    } else if (strcmp(p, "CONSOLE") == 0) {
401
        /* XXX WIN32 */
402
0
        ret = open_file(context, f, min, max, "/dev/console", "w", NULL,
403
0
                        FILEDISP_KEEPOPEN, 0);
404
0
    } else if (strncmp(p, "EFILE:", 5) == 0) {
405
0
        ret = open_file(context, f, min, max, p + sizeof("EFILE:") - 1, "a",
406
0
                        NULL, FILEDISP_IFEXISTS | FILEDISP_REOPEN, 1);
407
0
    } else if (strncmp(p, "EFILE=", 5) == 0) {
408
0
        ret = open_file(context, f, min, max, p + sizeof("EFILE=") - 1, "a",
409
0
                        NULL, FILEDISP_IFEXISTS | FILEDISP_KEEPOPEN, 1);
410
0
    } else if (strncmp(p, "FILE:", sizeof("FILE:") - 1) == 0) {
411
0
        ret = open_file(context, f, min, max, p + sizeof("FILE:") - 1, "a",
412
0
                        NULL, FILEDISP_REOPEN, 1);
413
0
    } else if (strncmp(p, "FILE=", sizeof("FILE=") - 1) == 0) {
414
0
        ret = open_file(context, f, min, max, p + sizeof("FILE=") - 1, "a",
415
0
                        NULL, FILEDISP_KEEPOPEN, 1);
416
0
    } else if (strncmp(p, "DEVICE:", sizeof("DEVICE:") - 1) == 0) {
417
0
        ret = open_file(context, f, min, max, p + sizeof("DEVICE:") - 1, "a",
418
0
                        NULL, FILEDISP_REOPEN, 0);
419
0
    } else if (strncmp(p, "DEVICE=", sizeof("DEVICE=") - 1) == 0) {
420
0
        ret = open_file(context, f, min, max, p + sizeof("DEVICE=") - 1, "a",
421
0
                        NULL, FILEDISP_KEEPOPEN, 0);
422
0
    } else if (strncmp(p, "SYSLOG", 6) == 0 && (p[6] == '\0' || p[6] == ':')) {
423
0
        char severity[128] = "";
424
0
        char facility[128] = "";
425
0
        p += 6;
426
0
        if (*p != '\0')
427
0
            p++;
428
0
        if (strsep_copy(&p, ":", severity, sizeof(severity)) != -1)
429
0
            strsep_copy(&p, ":", facility, sizeof(facility));
430
0
        if (*severity == '\0')
431
0
            strlcpy(severity, "ERR", sizeof(severity));
432
0
        if (*facility == '\0')
433
0
            strlcpy(facility, "AUTH", sizeof(facility));
434
0
        ret = open_syslog(context, f, min, max, severity, facility);
435
0
    } else {
436
0
        ret = EINVAL; /*XXX HEIM_ERR_LOG_PARSE*/
437
0
        heim_set_error_message(context, ret,
438
0
                               N_("unknown log type: %s", ""), p);
439
0
    }
440
0
    return ret;
441
0
}
442
443
heim_error_code
444
heim_openlog(heim_context context,
445
             const char *program,
446
             const char **specs,
447
             heim_log_facility **fac)
448
0
{
449
0
    heim_error_code ret;
450
451
0
    ret = heim_initlog(context, program, fac);
452
0
    if (ret)
453
0
        return ret;
454
455
0
    if (specs) {
456
0
        size_t i;
457
0
        for (i = 0; specs[i] && ret == 0; i++)
458
0
            ret = heim_addlog_dest(context, *fac, specs[i]);
459
0
    } else {
460
0
        ret = heim_addlog_dest(context, *fac, "SYSLOG");
461
0
    }
462
0
    return ret;
463
0
}
464
465
void
466
heim_closelog(heim_context context, heim_log_facility *fac)
467
0
{
468
0
    int i;
469
470
0
    if (!fac || heim_base_atomic_dec_32(&fac->refs))
471
0
        return;
472
0
    for (i = 0; i < fac->len; i++)
473
0
        (*fac->val[i].close_func)(fac->val[i].data);
474
0
    free(fac->val);
475
0
    free(fac->program);
476
0
    fac->val = NULL;
477
0
    fac->len = 0;
478
0
    fac->program = NULL;
479
0
    free(fac);
480
0
    return;
481
0
}
482
483
static void
484
format_time(heim_context context, time_t t, char *s, size_t len)
485
0
{
486
0
    struct tm *tm = heim_context_get_log_utc(context) ?
487
0
        gmtime(&t) : localtime(&t);
488
0
    if (tm && strftime(s, len, heim_context_get_time_fmt(context), tm))
489
0
        return;
490
0
    snprintf(s, len, "%ld", (long)t);
491
0
}
492
493
#undef __attribute__
494
#define __attribute__(X)
495
496
heim_error_code
497
heim_vlog_msg(heim_context context,
498
              heim_log_facility *fac,
499
              char **reply,
500
              int level,
501
              const char *fmt,
502
              va_list ap)
503
__attribute__ ((__format__ (__printf__, 5, 0)))
504
0
{
505
506
0
    char *msg = NULL;
507
0
    const char *actual = NULL;
508
0
    char buf[64];
509
0
    time_t t = 0;
510
0
    int i;
511
512
0
    if (!fac)
513
0
        fac = context->log_dest;
514
0
    for (i = 0; fac && i < fac->len; i++)
515
0
        if (fac->val[i].min <= level &&
516
0
            (fac->val[i].max < 0 || fac->val[i].max >= level)) {
517
0
            if (t == 0) {
518
0
                t = time(NULL);
519
0
                format_time(context, t, buf, sizeof(buf));
520
0
            }
521
0
            if (actual == NULL) {
522
0
                int ret = vasprintf(&msg, fmt, ap);
523
0
                if (ret < 0 || msg == NULL)
524
0
                    actual = fmt;
525
0
                else
526
0
                    actual = msg;
527
0
            }
528
0
            (*fac->val[i].log_func)(context, buf, actual, fac->val[i].data);
529
0
        }
530
0
    if (reply == NULL)
531
0
        free(msg);
532
0
    else
533
0
        *reply = msg;
534
0
    return 0;
535
0
}
536
537
heim_error_code
538
heim_vlog(heim_context context,
539
          heim_log_facility *fac,
540
          int level,
541
          const char *fmt,
542
          va_list ap)
543
__attribute__ ((__format__ (__printf__, 4, 0)))
544
0
{
545
0
    return heim_vlog_msg(context, fac, NULL, level, fmt, ap);
546
0
}
547
548
heim_error_code
549
heim_log_msg(heim_context context,
550
             heim_log_facility *fac,
551
             int level,
552
             char **reply,
553
             const char *fmt,
554
             ...)
555
__attribute__ ((__format__ (__printf__, 5, 6)))
556
0
{
557
0
    va_list ap;
558
0
    heim_error_code ret;
559
560
0
    va_start(ap, fmt);
561
0
    ret = heim_vlog_msg(context, fac, reply, level, fmt, ap);
562
0
    va_end(ap);
563
0
    return ret;
564
0
}
565
566
567
heim_error_code
568
heim_log(heim_context context,
569
         heim_log_facility *fac,
570
         int level,
571
         const char *fmt,
572
         ...)
573
__attribute__ ((__format__ (__printf__, 4, 5)))
574
0
{
575
0
    va_list ap;
576
0
    heim_error_code ret;
577
578
0
    va_start(ap, fmt);
579
0
    ret = heim_vlog(context, fac, level, fmt, ap);
580
0
    va_end(ap);
581
0
    return ret;
582
0
}
583
584
void
585
heim_debug(heim_context context,
586
           int level,
587
           const char *fmt,
588
           ...)
589
__attribute__ ((__format__ (__printf__, 3, 4)))
590
0
{
591
0
    heim_log_facility *fac;
592
0
    va_list ap;
593
594
0
    if (context == NULL ||
595
0
        (fac = heim_get_debug_dest(context)) == NULL)
596
0
        return;
597
598
0
    va_start(ap, fmt);
599
0
    heim_vlog(context, fac, level, fmt, ap);
600
0
    va_end(ap);
601
0
}
602
603
void
604
heim_vdebug(heim_context context,
605
            int level,
606
            const char *fmt,
607
            va_list ap)
608
__attribute__ ((__format__ (__printf__, 3, 0)))
609
0
{
610
0
    heim_log_facility *fac;
611
612
0
    if (context == NULL ||
613
0
        (fac = heim_get_debug_dest(context)) == NULL)
614
0
        return;
615
616
0
    heim_vlog(context, fac, level, fmt, ap);
617
0
}
618
619
heim_error_code
620
heim_have_debug(heim_context context, int level)
621
0
{
622
0
    return (context != NULL && heim_get_debug_dest(context) != NULL);
623
0
}
624
625
heim_error_code
626
heim_add_warn_dest(heim_context context, const char *program,
627
                   const char *log_spec)
628
0
{
629
0
    heim_log_facility *fac;
630
631
0
    heim_error_code ret;
632
633
0
    if ((fac = heim_get_warn_dest(context)) == NULL) {
634
0
        ret = heim_initlog(context, program, &fac);
635
0
        if (ret)
636
0
            return ret;
637
0
        heim_set_warn_dest(context, fac);
638
0
    }
639
640
0
    ret = heim_addlog_dest(context, fac, log_spec);
641
0
    if (ret)
642
0
        return ret;
643
0
    return 0;
644
0
}
645
646
heim_error_code
647
heim_add_debug_dest(heim_context context, const char *program,
648
                    const char *log_spec)
649
0
{
650
0
    heim_log_facility *fac;
651
0
    heim_error_code ret;
652
653
0
    if ((fac = heim_get_debug_dest(context)) == NULL) {
654
0
        ret = heim_initlog(context, program, &fac);
655
0
        if (ret)
656
0
            return ret;
657
0
        heim_set_debug_dest(context, fac);
658
0
    }
659
660
0
    ret = heim_addlog_dest(context, fac, log_spec);
661
0
    if (ret)
662
0
        return ret;
663
0
    return 0;
664
0
}
665
666
struct heim_audit_kv_tuple {
667
    heim_string_t key;
668
    heim_object_t value;
669
};
670
671
static struct heim_audit_kv_tuple zero_tuple;
672
673
static struct heim_audit_kv_tuple
674
fmtkv(int flags, const char *k, const char *fmt, va_list ap)
675
        __attribute__ ((__format__ (__printf__, 3, 0)))
676
0
{
677
0
    size_t i;
678
0
    ssize_t j;
679
0
    struct heim_audit_kv_tuple kv;
680
0
    char *value;
681
0
    char *value_vis;
682
683
0
    j = vasprintf(&value, fmt, ap);
684
0
    if (j < 0 || value == NULL)
685
0
  return zero_tuple;
686
687
    /* We optionally eat the whitespace. */
688
689
0
    if (flags & HEIM_SVC_AUDIT_EATWHITE) {
690
0
  for (i=0, j=0; value[i]; i++)
691
0
      if (value[i] != ' ' && value[i] != '\t')
692
0
    value[j++] = value[i];
693
0
  value[j] = '\0';
694
0
    }
695
696
0
    if (flags & (HEIM_SVC_AUDIT_VIS | HEIM_SVC_AUDIT_VISLAST)) {
697
0
        int vis_flags = VIS_CSTYLE | VIS_OCTAL | VIS_NL;
698
699
0
        if (flags & HEIM_SVC_AUDIT_VIS)
700
0
            vis_flags |= VIS_WHITE;
701
0
  value_vis = malloc((j + 1) * 4 + 1);
702
0
        if (value_vis)
703
0
            strvisx(value_vis, value, j, vis_flags);
704
0
  free(value);
705
0
        if (value_vis == NULL)
706
0
            return zero_tuple;
707
0
    } else
708
0
  value_vis = value;
709
710
0
    if (k)
711
0
  kv.key = heim_string_create(k);
712
0
    else
713
0
  kv.key = NULL;
714
0
    kv.value = heim_string_ref_create(value_vis, free);
715
716
0
    return kv;
717
0
}
718
719
void
720
heim_audit_vaddreason(heim_svc_req_desc r, const char *fmt, va_list ap)
721
  __attribute__ ((__format__ (__printf__, 2, 0)))
722
0
{
723
0
    struct heim_audit_kv_tuple kv;
724
725
0
    kv = fmtkv(HEIM_SVC_AUDIT_VISLAST, NULL, fmt, ap);
726
0
    if (kv.value == NULL) {
727
0
        heim_log(r->hcontext, r->logf, 1, "heim_audit_vaddreason: "
728
0
                 "failed to add reason (out of memory)");
729
0
        return;
730
0
    }
731
732
0
    heim_log(r->hcontext, r->logf, 7, "heim_audit_vaddreason(): "
733
0
             "adding reason %s", heim_string_get_utf8(kv.value));
734
0
    if (r->reason) {
735
0
        heim_string_t str2;
736
737
0
        str2 = heim_string_create_with_format("%s: %s",
738
0
                                              heim_string_get_utf8(kv.value),
739
0
                                              heim_string_get_utf8(r->reason));
740
0
        if (str2) {
741
0
            heim_release(kv.value);
742
0
            kv.value = str2;
743
0
        }
744
0
    }
745
0
    heim_release(r->reason);
746
0
    r->reason = kv.value;
747
0
}
748
749
void
750
heim_audit_addreason(heim_svc_req_desc r, const char *fmt, ...)
751
  __attribute__ ((__format__ (__printf__, 2, 3)))
752
0
{
753
0
    va_list ap;
754
755
0
    va_start(ap, fmt);
756
0
    heim_audit_vaddreason(r, fmt, ap);
757
0
    va_end(ap);
758
0
}
759
760
size_t
761
addkv(heim_svc_req_desc r, heim_object_t key, heim_object_t value)
762
0
{
763
0
    size_t index;
764
0
    heim_object_t obj;
765
766
0
    obj = heim_dict_get_value(r->kv, key);
767
0
    if (obj) {
768
0
  if (heim_get_tid(obj) == HEIM_TID_ARRAY) {
769
0
      index = heim_array_get_length(obj);
770
0
      heim_array_append_value(obj, value);
771
0
  } else {
772
0
      heim_array_t array = heim_array_create();
773
774
0
      index = 1;
775
0
      heim_array_append_value(array, obj);
776
0
      heim_array_append_value(array, value);
777
0
      heim_dict_set_value(r->kv, key, array);
778
0
      heim_release(array); /* retained by r->kv */
779
0
  }
780
0
    } else {
781
0
  index = 0;
782
0
  heim_dict_set_value(r->kv, key, value);
783
0
    }
784
785
0
    return index;
786
0
}
787
788
/*
789
 * add a key-value token. if the key already exists, the value is
790
 * promoted to an array of values.
791
 */
792
793
void
794
heim_audit_vaddkv(heim_svc_req_desc r, int flags, const char *k,
795
      const char *fmt, va_list ap)
796
  __attribute__ ((__format__ (__printf__, 4, 0)))
797
0
{
798
0
    struct heim_audit_kv_tuple kv;
799
0
    size_t index;
800
801
0
    kv = fmtkv(flags, k, fmt, ap);
802
0
    if (kv.key == NULL || kv.value == NULL) {
803
0
        heim_log(r->hcontext, r->logf, 1, "heim_audit_vaddkv: "
804
0
                 "failed to add kv pair (out of memory)");
805
0
  heim_release(kv.key);
806
0
  heim_release(kv.value);
807
0
        return;
808
0
    }
809
810
0
    index = addkv(r, kv.key, kv.value);
811
812
0
    heim_log(r->hcontext, r->logf, 7, "heim_audit_vaddkv(): "
813
0
             "kv pair[%zu] %s=%s", index,
814
0
       heim_string_get_utf8(kv.key), heim_string_get_utf8(kv.value));
815
816
0
    heim_release(kv.key);
817
0
    heim_release(kv.value);
818
0
}
819
820
void
821
heim_audit_addkv(heim_svc_req_desc r, int flags, const char *k,
822
     const char *fmt, ...)
823
  __attribute__ ((__format__ (__printf__, 4, 5)))
824
0
{
825
0
    va_list ap;
826
827
0
    va_start(ap, fmt);
828
0
    heim_audit_vaddkv(r, flags, k, fmt, ap);
829
0
    va_end(ap);
830
0
}
831
832
void
833
heim_audit_addkv_timediff(heim_svc_req_desc r, const char *k,
834
        const struct timeval *start,
835
        const struct timeval *end)
836
0
{
837
0
    time_t sec;
838
0
    int usec;
839
0
    const char *sign = "";
840
841
0
    if (end->tv_sec > start->tv_sec ||
842
0
  (end->tv_sec == start->tv_sec && end->tv_usec >= start->tv_usec)) {
843
0
  sec  = end->tv_sec  - start->tv_sec;
844
0
  usec = end->tv_usec - start->tv_usec;
845
0
    } else {
846
0
  sec  = start->tv_sec  - end->tv_sec;
847
0
  usec = start->tv_usec - end->tv_usec;
848
0
  sign = "-";
849
0
    }
850
851
0
    if (usec < 0) {
852
0
  usec += 1000000;
853
0
  sec  -= 1;
854
0
    }
855
856
0
    heim_audit_addkv(r, 0, k, "%s%ld.%06d", sign, (long)sec, usec);
857
0
}
858
859
void
860
heim_audit_setkv_bool(heim_svc_req_desc r, const char *k, int v)
861
0
{
862
0
    heim_string_t key = heim_string_create(k);
863
0
    heim_number_t value;
864
865
0
    if (key == NULL)
866
0
  return;
867
868
0
    heim_log(r->hcontext, r->logf, 7, "heim_audit_setkv_bool(): "
869
0
       "setting kv pair %s=%s", k, v ? "true" : "false");
870
871
0
    value = heim_bool_create(v);
872
0
    heim_dict_set_value(r->kv, key, value);
873
0
    heim_release(key);
874
0
    heim_release(value);
875
0
}
876
877
void
878
heim_audit_addkv_number(heim_svc_req_desc r, const char *k, int64_t v)
879
0
{
880
0
    heim_string_t key = heim_string_create(k);
881
0
    heim_number_t value;
882
883
0
    if (key == NULL)
884
0
  return;
885
886
0
    heim_log(r->hcontext, r->logf, 7, "heim_audit_addkv_number(): "
887
0
       "adding kv pair %s=%lld", k, (long long)v);
888
889
0
    value = heim_number_create(v);
890
0
    addkv(r, key, value);
891
0
    heim_release(key);
892
0
    heim_release(value);
893
0
}
894
895
void
896
heim_audit_setkv_number(heim_svc_req_desc r, const char *k, int64_t v)
897
0
{
898
0
    heim_string_t key = heim_string_create(k);
899
0
    heim_number_t value;
900
901
0
    if (key == NULL)
902
0
  return;
903
904
0
    heim_log(r->hcontext, r->logf, 7, "heim_audit_setkv_number(): "
905
0
       "setting kv pair %s=%lld", k, (long long)v);
906
907
0
    value = heim_number_create(v);
908
0
    heim_dict_set_value(r->kv, key, value);
909
0
    heim_release(key);
910
0
    heim_release(value);
911
0
}
912
913
void
914
heim_audit_addkv_object(heim_svc_req_desc r, const char *k, heim_object_t value)
915
0
{
916
0
    heim_string_t key = heim_string_create(k);
917
0
    heim_string_t descr;
918
919
0
    if (key == NULL)
920
0
  return;
921
922
0
    descr = heim_json_copy_serialize(value, HEIM_JSON_F_NO_DATA_DICT, NULL);
923
0
    heim_log(r->hcontext, r->logf, 7, "heim_audit_addkv_object(): "
924
0
       "adding kv pair %s=%s",
925
0
       k, descr ? heim_string_get_utf8(descr) : "<unprintable>");
926
0
    addkv(r, key, value);
927
0
    heim_release(key);
928
0
    heim_release(descr);
929
0
}
930
931
void
932
heim_audit_setkv_object(heim_svc_req_desc r, const char *k, heim_object_t value)
933
0
{
934
0
    heim_string_t key = heim_string_create(k);
935
0
    heim_string_t descr;
936
937
0
    if (key == NULL)
938
0
  return;
939
940
0
    descr = heim_json_copy_serialize(value, HEIM_JSON_F_NO_DATA_DICT, NULL);
941
0
    heim_log(r->hcontext, r->logf, 7, "heim_audit_setkv_object(): "
942
0
       "setting kv pair %s=%s",
943
0
       k, descr ? heim_string_get_utf8(descr) : "<unprintable>");
944
0
    heim_dict_set_value(r->kv, key, value);
945
0
    heim_release(key);
946
0
    heim_release(descr);
947
0
}
948
949
heim_object_t
950
heim_audit_getkv(heim_svc_req_desc r, const char *k)
951
0
{
952
0
    heim_string_t key;
953
0
    heim_object_t value;
954
955
0
    key = heim_string_create(k);
956
0
    if (key == NULL)
957
0
  return NULL;
958
959
0
    value = heim_dict_get_value(r->kv, key);
960
0
    heim_release(key);
961
0
    return value;
962
0
}
963
964
struct heim_audit_kv_buf {
965
    char buf[1024];
966
    size_t pos;
967
    heim_object_t iter;
968
};
969
970
static void
971
audit_trail_iterator(heim_object_t key, heim_object_t value, void *arg);
972
973
static void
974
audit_trail_iterator_array(heim_object_t value, void *arg, int *stop)
975
0
{
976
0
    struct heim_audit_kv_buf *kvb = arg;
977
978
0
    audit_trail_iterator(kvb->iter, value, kvb);
979
0
}
980
981
static void
982
audit_trail_iterator(heim_object_t key, heim_object_t value, void *arg)
983
0
{
984
0
    struct heim_audit_kv_buf *kvb = arg;
985
0
    char num[32];
986
0
    const char *k = heim_string_get_utf8(key), *v = NULL;
987
0
    char *b64 = NULL;
988
989
0
    if (k == NULL || *k == '#') /* # keys are hidden */
990
0
  return;
991
992
0
    switch (heim_get_tid(value)) {
993
0
    case HEIM_TID_STRING:
994
0
  v = heim_string_get_utf8(value);
995
0
  break;
996
0
    case HEIM_TID_NUMBER:
997
0
  snprintf(num, sizeof(num), "%lld", (long long)heim_number_get_long(value));
998
0
  v = num;
999
0
  break;
1000
0
    case HEIM_TID_NULL:
1001
0
  v = "null";
1002
0
  break;
1003
0
    case HEIM_TID_BOOL:
1004
0
  v = heim_bool_val(value) ? "true" : "false";
1005
0
  break;
1006
0
    case HEIM_TID_ARRAY:
1007
0
  if (kvb->iter)
1008
0
      break; /* arrays cannot be nested */
1009
1010
0
  kvb->iter = key;
1011
0
  heim_array_iterate_f(value, kvb, audit_trail_iterator_array);
1012
0
  kvb->iter = NULL;
1013
0
  break;
1014
0
    case HEIM_TID_DATA: {
1015
0
  const heim_octet_string *data = heim_data_get_data(value);
1016
0
  if (rk_base64_encode(data->data, data->length, &b64) >= 0)
1017
0
      v = b64;
1018
0
  break;
1019
0
    }
1020
0
    default:
1021
0
  break;
1022
0
    }
1023
1024
0
    if (v == NULL)
1025
0
  return;
1026
1027
0
    if (kvb->pos < sizeof(kvb->buf) - 1)
1028
0
  kvb->buf[kvb->pos++] = ' ';
1029
0
    for (; *k && kvb->pos < sizeof(kvb->buf) - 1; kvb->pos++)
1030
0
  kvb->buf[kvb->pos] = *k++;
1031
0
    if (kvb->pos < sizeof(kvb->buf) - 1)
1032
0
  kvb->buf[kvb->pos++] = '=';
1033
0
    for (; *v && kvb->pos < sizeof(kvb->buf) - 1; kvb->pos++)
1034
0
  kvb->buf[kvb->pos] = *v++;
1035
1036
0
    free(b64);
1037
0
}
1038
1039
void
1040
heim_audit_trail(heim_svc_req_desc r, heim_error_code ret, const char *retname)
1041
0
{
1042
0
    const char *retval;
1043
0
    struct heim_audit_kv_buf kvb;
1044
0
    char retvalbuf[30]; /* Enough for UNKNOWN-%d */
1045
1046
0
#define CASE(x) case x : retval = #x; break
1047
0
    if (retname) {
1048
0
        retval = retname;
1049
0
    } else switch (ret ? ret : r->error_code) {
1050
0
    CASE(ENOMEM);
1051
0
    CASE(ENOENT);
1052
0
    CASE(EACCES);
1053
0
    case 0:
1054
0
  retval = "SUCCESS";
1055
0
  break;
1056
0
    default:
1057
        /* Wish we had a com_err number->symbolic name function */
1058
0
        (void) snprintf(retvalbuf, sizeof(retvalbuf), "UNKNOWN-%d",
1059
0
                        ret ? ret : r->error_code);
1060
0
  retval = retvalbuf;
1061
0
  break;
1062
0
    }
1063
1064
0
    heim_audit_addkv_timediff(r, "elapsed", &r->tv_start, &r->tv_end);
1065
0
    if (r->e_text && r->kv)
1066
0
  heim_audit_addkv(r, HEIM_SVC_AUDIT_VIS, "e-text", "%s", r->e_text);
1067
1068
0
    memset(&kvb, 0, sizeof(kvb));
1069
0
    if (r->kv)
1070
0
        heim_dict_iterate_f(r->kv, &kvb, audit_trail_iterator);
1071
0
    kvb.buf[kvb.pos] = '\0';
1072
1073
0
    heim_log(r->hcontext, r->logf, 3, "%s %s %s %s %s%s%s%s",
1074
0
             r->reqtype, retval, r->from,
1075
0
             r->cname ? r->cname : "<unknown>",
1076
0
             r->sname ? r->sname : "<unknown>",
1077
0
             kvb.buf, r->reason ? " reason=" : "",
1078
0
             r->reason ? heim_string_get_utf8(r->reason) : "");
1079
0
}