Move KDC audit functionality to lib/base/
[metze/heimdal/wip.git] / lib / base / context.c
1 /*
2  * Copyright (c) 2020 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include "baselocl.h"
35
36 #undef __attribute__
37 #define __attribute__(X)
38
39 struct heim_context_s {
40     heim_log_facility       *log_dest;
41     heim_log_facility       *warn_dest;
42     heim_log_facility       *debug_dest;
43     char                    *time_fmt;
44     unsigned int            log_utc:1;
45     unsigned int            homedir_access:1;
46     heim_err_cb_context     error_context;
47     heim_err_cb_clear_msg    clear_error_message;
48     heim_err_cb_free_msg    free_error_message;
49     heim_err_cb_get_msg     get_error_message;
50     heim_err_cb_set_msg     set_error_message;
51     const char              *unknown_error;
52     const char              *success;
53 };
54
55 heim_context
56 heim_context_init(void)
57 {
58     heim_context context;
59
60     if ((context = calloc(1, sizeof(*context))) == NULL)
61         return NULL;
62
63     context->log_utc = 1;
64     context->clear_error_message = NULL;
65     context->free_error_message = NULL;
66     context->get_error_message = NULL;
67     context->set_error_message = NULL;
68     context->error_context = NULL;
69     context->unknown_error = "Unknown error";
70     context->success = "Success";
71     context->debug_dest = NULL;
72     context->warn_dest = NULL;
73     context->log_dest = NULL;
74     context->time_fmt = NULL;
75     return context;
76 }
77
78 void
79 heim_context_free(heim_context *contextp)
80 {
81     heim_context context = *contextp;
82
83     *contextp = NULL;
84     if (!context)
85         return;
86     heim_closelog(context, context->debug_dest);
87     heim_closelog(context, context->warn_dest);
88     heim_closelog(context, context->log_dest);
89     free(context->time_fmt);
90     free(context);
91 }
92
93 void
94 heim_context_set_msg_cb(heim_context context,
95                         heim_err_cb_context cb_context,
96                         heim_err_cb_clear_msg cb_clear_msg,
97                         heim_err_cb_free_msg cb_free_msg,
98                         heim_err_cb_get_msg cb_get_msg,
99                         heim_err_cb_set_msg cb_set_msg)
100 {
101     context->error_context = cb_context;
102     context->clear_error_message = cb_clear_msg;
103     context->free_error_message = cb_free_msg;
104     context->set_error_message = cb_set_msg;
105     context->get_error_message = cb_get_msg;
106 }
107
108 heim_error_code
109 heim_context_set_time_fmt(heim_context context, const char *fmt)
110 {
111     char *s;
112
113     if (fmt == NULL) {
114         free(context->time_fmt);
115         return 0;
116     }
117     if ((s = strdup(fmt)) == NULL)
118         return heim_enomem(context);
119     free(context->time_fmt);
120     context->time_fmt = s;
121     return 0;
122 }
123
124 const char *
125 heim_context_get_time_fmt(heim_context context)
126 {
127     return context->time_fmt ? context->time_fmt : "%Y-%m-%dT%H:%M:%S";
128 }
129
130 unsigned int
131 heim_context_set_log_utc(heim_context context, unsigned int log_utc)
132 {
133     unsigned int old = context->log_utc;
134
135     context->log_utc = log_utc ? 1 : 0;
136     return old;
137 }
138
139 int
140 heim_context_get_log_utc(heim_context context)
141 {
142     return context->log_utc;
143 }
144
145 unsigned int
146 heim_context_set_homedir_access(heim_context context, unsigned int homedir_access)
147 {
148     unsigned int old = context->homedir_access;
149
150     context->homedir_access = homedir_access ? 1 : 0;
151     return old;
152 }
153
154 unsigned int
155 heim_context_get_homedir_access(heim_context context)
156 {
157     return context->homedir_access;
158 }
159
160 void
161 heim_clear_error_message(heim_context context)
162 {
163     if (context != NULL && context->clear_error_message != NULL)
164         context->clear_error_message(context->error_context);
165 }
166
167 void
168 heim_free_error_message(heim_context context, const char *msg)
169 {
170     if (context != NULL && context->free_error_message != NULL &&
171         msg != context->unknown_error && msg != context->success)
172         context->free_error_message(context->error_context, msg);
173 }
174
175 const char *
176 heim_get_error_message(heim_context context, heim_error_code ret)
177 {
178     if (context != NULL && context->get_error_message != NULL)
179         return context->get_error_message(context->error_context, ret);
180     if (ret)
181         return context->unknown_error;
182     return context->success;
183 }
184
185 void
186 heim_set_error_message(heim_context context, heim_error_code ret,
187                        const char *fmt, ...)
188     __attribute__ ((__format__ (__printf__, 3, 4)))
189 {
190     va_list ap;
191
192     va_start(ap, fmt);
193     heim_vset_error_message(context, ret, fmt, ap);
194     va_end(ap);
195 }
196
197 void
198 heim_vset_error_message(heim_context context, heim_error_code ret,
199                         const char *fmt, va_list args)
200     __attribute__ ((__format__ (__printf__, 3, 0)))
201 {
202     if (context != NULL && context->set_error_message != NULL)
203         context->set_error_message(context->error_context, ret, fmt, args);
204 }
205
206 heim_error_code
207 heim_enomem(heim_context context)
208 {
209     heim_set_error_message(context, ENOMEM, "malloc: out of memory");
210     return ENOMEM;
211 }
212
213 heim_log_facility *
214 heim_get_log_dest(heim_context context)
215 {
216     return context->log_dest;
217 }
218
219 heim_log_facility *
220 heim_get_warn_dest(heim_context context)
221 {
222     return context->warn_dest;
223 }
224
225 heim_log_facility *
226 heim_get_debug_dest(heim_context context)
227 {
228     return context->debug_dest;
229 }
230
231 heim_error_code
232 heim_set_log_dest(heim_context context, heim_log_facility *fac)
233 {
234     context->log_dest = fac;
235     return 0;
236 }
237
238 heim_error_code
239 heim_set_warn_dest(heim_context context, heim_log_facility *fac)
240 {
241     context->warn_dest = fac;
242     return 0;
243 }
244
245 heim_error_code
246 heim_set_debug_dest(heim_context context, heim_log_facility *fac)
247 {
248     context->debug_dest = fac;
249     return 0;
250 }
251
252 #define PATH_SEP ":"
253
254 static heim_error_code
255 add_file(char ***pfilenames, int *len, char *file)
256 {
257     char **pp = *pfilenames;
258     int i;
259
260     for(i = 0; i < *len; i++) {
261         if(strcmp(pp[i], file) == 0) {
262             free(file);
263             return 0;
264         }
265     }
266
267     pp = realloc(*pfilenames, (*len + 2) * sizeof(*pp));
268     if (pp == NULL) {
269         free(file);
270         return ENOMEM;
271     }
272
273     pp[*len] = file;
274     pp[*len + 1] = NULL;
275     *pfilenames = pp;
276     *len += 1;
277     return 0;
278 }
279
280 #ifdef WIN32
281 static char *
282 get_default_config_config_files_from_registry(void)
283 {
284     static const char *KeyName = "Software\\Heimdal"; /* XXX #define this */
285     char *config_file = NULL;
286     LONG rcode;
287     HKEY key;
288
289     rcode = RegOpenKeyEx(HKEY_CURRENT_USER, KeyName, 0, KEY_READ, &key);
290     if (rcode == ERROR_SUCCESS) {
291         config_file = heim_parse_reg_value_as_multi_string(NULL, key, "config",
292                                                            REG_NONE, 0, PATH_SEP);
293         RegCloseKey(key);
294     }
295
296     if (config_file)
297         return config_file;
298
299     rcode = RegOpenKeyEx(HKEY_LOCAL_MACHINE, KeyName, 0, KEY_READ, &key);
300     if (rcode == ERROR_SUCCESS) {
301         config_file = heim_parse_reg_value_as_multi_string(NULL, key, "config",
302                                                            REG_NONE, 0, PATH_SEP);
303         RegCloseKey(key);
304     }
305
306     return config_file;
307 }
308 #endif
309
310 heim_error_code
311 heim_prepend_config_files(const char *filelist,
312                           char **pq,
313                           char ***ret_pp)
314 {
315     heim_error_code ret;
316     const char *p, *q;
317     char **pp;
318     int len;
319     char *fn;
320
321     pp = NULL;
322
323     len = 0;
324     p = filelist;
325     while(1) {
326         ssize_t l;
327         q = p;
328         l = strsep_copy(&q, PATH_SEP, NULL, 0);
329         if(l == -1)
330             break;
331         fn = malloc(l + 1);
332         if(fn == NULL) {
333             heim_free_config_files(pp);
334             return ENOMEM;
335         }
336         (void) strsep_copy(&p, PATH_SEP, fn, l + 1);
337         ret = add_file(&pp, &len, fn);
338         if (ret) {
339             heim_free_config_files(pp);
340             return ret;
341         }
342     }
343
344     if (pq != NULL) {
345         int i;
346
347         for (i = 0; pq[i] != NULL; i++) {
348             fn = strdup(pq[i]);
349             if (fn == NULL) {
350                 heim_free_config_files(pp);
351                 return ENOMEM;
352             }
353             ret = add_file(&pp, &len, fn);
354             if (ret) {
355                 heim_free_config_files(pp);
356                 return ret;
357             }
358         }
359     }
360
361     *ret_pp = pp;
362     return 0;
363 }
364
365 heim_error_code
366 heim_prepend_config_files_default(const char *prepend,
367                                   const char *def,
368                                   const char *envvar,
369                                   char ***pfilenames)
370 {
371     heim_error_code ret;
372     char **defpp, **pp = NULL;
373
374     ret = heim_get_default_config_files(def, envvar, &defpp);
375     if (ret)
376         return ret;
377
378     ret = heim_prepend_config_files(prepend, defpp, &pp);
379     heim_free_config_files(defpp);
380     if (ret) {
381         return ret;
382     }
383     *pfilenames = pp;
384     return 0;
385 }
386
387 heim_error_code
388 heim_get_default_config_files(const char *def,
389                               const char *envvar,
390                               char ***pfilenames)
391 {
392     const char *files = NULL;
393
394     files = secure_getenv(envvar);
395
396 #ifdef _WIN32
397     if (files == NULL) {
398         char * reg_files;
399         reg_files = get_default_config_config_files_from_registry();
400         if (reg_files != NULL) {
401             heim_error_code code;
402
403             code = heim_prepend_config_files(reg_files, NULL, pfilenames);
404             free(reg_files);
405
406             return code;
407         }
408     }
409 #endif
410
411     if (files == NULL)
412         files = def;
413     return heim_prepend_config_files(files, NULL, pfilenames);
414 }
415
416 #ifdef _WIN32
417 #define REGPATH_KERBEROS "SOFTWARE\\Kerberos"
418 #define REGPATH_HEIMDAL  "SOFTWARE\\Heimdal"
419 #endif
420
421 heim_error_code
422 heim_set_config_files(heim_context context, char **filenames,
423                       heim_config_binding **res)
424 {
425     heim_error_code ret = 0;
426
427     *res = NULL;
428     while (filenames != NULL && *filenames != NULL && **filenames != '\0') {
429         ret = heim_config_parse_file_multi(context, *filenames, res);
430         if (ret != 0 && ret != ENOENT && ret != EACCES && ret != EPERM
431             && ret != HEIM_ERR_CONFIG_BADFORMAT) {
432             heim_config_file_free(context, *res);
433             *res = NULL;
434             return ret;
435         }
436         filenames++;
437     }
438
439 #ifdef _WIN32
440     /*
441      * We always ignored errors from loading from the registry, so we still do.
442      */
443     heim_load_config_from_registry(context, REGPATH_KERBEROS,
444                                    REGPATH_HEIMDAL, res);
445
446 #endif
447     return 0;
448 }
449
450 void
451 heim_free_config_files(char **filenames)
452 {
453     char **p;
454
455     for (p = filenames; p && *p != NULL; p++)
456         free(*p);
457     free(filenames);
458 }