1 /*
2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6
7 /*
8 * lib/kdb/kdb_db2.c
9 *
10 * Copyright 1997,2006 by the Massachusetts Institute of Technology.
11 * All Rights Reserved.
12 *
13 * Export of this software from the United States of America may
14 * require a specific license from the United States Government.
15 * It is the responsibility of any person or organization contemplating
16 * export to obtain such a license before exporting.
17 *
18 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
19 * distribute this software and its documentation for any purpose and
20 * without fee is hereby granted, provided that the above copyright
21 * notice appear in all copies and that both that copyright notice and
22 * this permission notice appear in supporting documentation, and that
23 * the name of M.I.T. not be used in advertising or publicity pertaining
24 * to distribution of the software without specific, written prior
25 * permission. Furthermore if you modify this software you must label
26 * your software as modified software and not distribute it in such a
27 * fashion that it might be confused with the original M.I.T. software.
28 * M.I.T. makes no representations about the suitability of
29 * this software for any purpose. It is provided "as is" without express
30 * or implied warranty.
31 *
32 */
33
34 /*
35 * Copyright (C) 1998 by the FundsXpress, INC.
36 *
37 * All rights reserved.
38 *
39 * Export of this software from the United States of America may require
40 * a specific license from the United States Government. It is the
41 * responsibility of any person or organization contemplating export to
42 * obtain such a license before exporting.
43 *
44 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
45 * distribute this software and its documentation for any purpose and
46 * without fee is hereby granted, provided that the above copyright
47 * notice appear in all copies and that both that copyright notice and
48 * this permission notice appear in supporting documentation, and that
49 * the name of FundsXpress. not be used in advertising or publicity pertaining
50 * to distribution of the software without specific, written prior
51 * permission. FundsXpress makes no representations about the suitability of
52 * this software for any purpose. It is provided "as is" without express
53 * or implied warranty.
54 *
55 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
56 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
57 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
58 */
59
60 #include "k5-int.h"
61 #include <kdb_log.h>
62
63 #if HAVE_UNISTD_H
64 #include <unistd.h>
65 #endif
66
67 #include <db.h>
68 #include <stdio.h>
69 #include <errno.h>
70 #include <utime.h>
71 #include "kdb5.h"
72 #include "kdb_db2.h"
73 #include "kdb_xdr.h"
74 #include "policy_db.h"
75 #include <libintl.h>
76
77 #define KDB_DB2_DATABASE_NAME "database_name"
78
79 #include "kdb_db2.h"
80
81 static char *gen_dbsuffix(char *, char *);
82
83 static krb5_error_code krb5_db2_db_start_update(krb5_context);
84 static krb5_error_code krb5_db2_db_end_update(krb5_context);
85
86 static krb5_error_code krb5_db2_db_set_name(krb5_context, char *, int);
87
88 krb5_error_code krb5_db2_db_lock(krb5_context, int);
89
90 static krb5_error_code krb5_db2_db_set_hashfirst(krb5_context, int);
91
92 /*
93 * Solaris Kerberos
94 * Extra error handling
95 */
96 char errbuf[1024];
97 static void krb5_db2_prepend_err_str(krb5_context , const char *,
98 krb5_error_code, krb5_error_code);
99
100 static char default_db_name[] = DEFAULT_KDB_FILE;
101
102 /*
103 * Locking:
104 *
105 * There are two distinct locking protocols used. One is designed to
106 * lock against processes (the admin_server, for one) which make
107 * incremental changes to the database; the other is designed to lock
108 * against utilities (kdb5_edit, kpropd, kdb5_convert) which replace the
109 * entire database in one fell swoop.
110 *
111 * The first locking protocol is implemented using flock() in the
112 * krb_dbl_lock() and krb_dbl_unlock routines.
113 *
114 * The second locking protocol is necessary because DBM "files" are
115 * actually implemented as two separate files, and it is impossible to
116 * atomically rename two files simultaneously. It assumes that the
117 * database is replaced only very infrequently in comparison to the time
118 * needed to do a database read operation.
119 *
120 * A third file is used as a "version" semaphore; the modification
121 * time of this file is the "version number" of the database.
122 * At the start of a read operation, the reader checks the version
123 * number; at the end of the read operation, it checks again. If the
124 * version number changed, or if the semaphore was nonexistant at
125 * either time, the reader sleeps for a second to let things
126 * stabilize, and then tries again; if it does not succeed after
127 * KRB5_DBM_MAX_RETRY attempts, it gives up.
128 *
129 * On update, the semaphore file is deleted (if it exists) before any
130 * update takes place; at the end of the update, it is replaced, with
131 * a version number strictly greater than the version number which
132 * existed at the start of the update.
133 *
134 * If the system crashes in the middle of an update, the semaphore
135 * file is not automatically created on reboot; this is a feature, not
136 * a bug, since the database may be inconsistant. Note that the
137 * absence of a semaphore file does not prevent another _update_ from
138 * taking place later. Database replacements take place automatically
139 * only on slave servers; a crash in the middle of an update will be
140 * fixed by the next slave propagation. A crash in the middle of an
141 * update on the master would be somewhat more serious, but this would
142 * likely be noticed by an administrator, who could fix the problem and
143 * retry the operation.
144 */
145
146 #define free_dbsuffix(name) free(name)
147
148 /*
149 * Routines to deal with context.
150 */
151 #define k5db2_inited(c) (c && c->db_context \
152 && ((kdb5_dal_handle*)c->db_context)->db_context \
153 && ((krb5_db2_context *) ((kdb5_dal_handle*)c->db_context)->db_context)->db_inited)
154
155 static krb5_error_code
krb5_db2_get_db_opt(char * input,char ** opt,char ** val)156 krb5_db2_get_db_opt(char *input, char **opt, char **val)
157 {
158 char *pos = strchr(input, '=');
159 if (pos == NULL) {
160 *opt = NULL;
161 *val = strdup(input);
162 if (*val == NULL) {
163 return ENOMEM;
164 }
165 } else {
166 *opt = malloc((pos - input) + 1);
167 *val = strdup(pos + 1);
168 if (!*opt || !*val) {
169 return ENOMEM;
170 }
171 memcpy(*opt, input, pos - input);
172 (*opt)[pos - input] = '\0';
173 }
174 return (0);
175
176 }
177
178 /*
179 * Restore the default context.
180 */
181 static void
k5db2_clear_context(krb5_db2_context * dbctx)182 k5db2_clear_context(krb5_db2_context *dbctx)
183 {
184 /*
185 * Free any dynamically allocated memory. File descriptors and locks
186 * are the caller's problem.
187 */
188 if (dbctx->db_lf_name)
189 free(dbctx->db_lf_name);
190 if (dbctx->db_name && (dbctx->db_name != default_db_name))
191 free(dbctx->db_name);
192 /*
193 * Clear the structure and reset the defaults.
194 */
195 memset((char *) dbctx, 0, sizeof(krb5_db2_context));
196 dbctx->db_name = default_db_name;
197 dbctx->db_nb_locks = FALSE;
198 dbctx->tempdb = FALSE;
199 }
200
201 static krb5_error_code
k5db2_init_context(krb5_context context)202 k5db2_init_context(krb5_context context)
203 {
204 krb5_db2_context *db_ctx;
205 kdb5_dal_handle *dal_handle;
206
207 dal_handle = (kdb5_dal_handle *) context->db_context;
208
209 if (dal_handle->db_context == NULL) {
210 db_ctx = (krb5_db2_context *) malloc(sizeof(krb5_db2_context));
211 if (db_ctx == NULL)
212 return ENOMEM;
213 else {
214 memset((char *) db_ctx, 0, sizeof(krb5_db2_context));
215 k5db2_clear_context((krb5_db2_context *) db_ctx);
216 dal_handle->db_context = (void *) db_ctx;
217 }
218 }
219 return (0);
220 }
221
222 /*
223 * Utility routine: generate name of database file.
224 */
225
226 static char *
gen_dbsuffix(char * db_name,char * sfx)227 gen_dbsuffix(char *db_name, char *sfx)
228 {
229 char *dbsuffix;
230
231 if (sfx == NULL)
232 return ((char *) NULL);
233
234 dbsuffix = malloc(strlen(db_name) + strlen(sfx) + 1);
235 if (!dbsuffix)
236 return (0);
237 /*LINTED*/
238 (void) strcpy(dbsuffix, db_name);
239 /*LINTED*/
240 (void) strcat(dbsuffix, sfx);
241 return dbsuffix;
242 }
243
244 static DB *
k5db2_dbopen(krb5_db2_context * dbc,char * fname,int flags,int mode,int tempdb)245 k5db2_dbopen(krb5_db2_context *dbc, char *fname, int flags, int mode, int tempdb)
246 {
247 DB *db;
248 BTREEINFO bti;
249 HASHINFO hashi;
250 bti.flags = 0;
251 bti.cachesize = 0;
252 bti.psize = 4096;
253 bti.lorder = 0;
254 bti.minkeypage = 0;
255 bti.compare = NULL;
256 bti.prefix = NULL;
257
258 if (tempdb) {
259 fname = gen_dbsuffix(fname, "~");
260 } else {
261 fname = strdup(fname);
262 }
263 if (fname == NULL)
264 {
265 errno = ENOMEM;
266 return NULL;
267 }
268
269
270 hashi.bsize = 4096;
271 hashi.cachesize = 0;
272 hashi.ffactor = 40;
273 hashi.hash = NULL;
274 hashi.lorder = 0;
275 hashi.nelem = 1;
276
277 db = dbopen(fname, flags, mode,
278 dbc->hashfirst ? DB_HASH : DB_BTREE,
279 dbc->hashfirst ? (void *) &hashi : (void *) &bti);
280 if (db != NULL) {
281 free(fname);
282 return db;
283 }
284 switch (errno) {
285 #ifdef EFTYPE
286 case EFTYPE:
287 #endif
288 case EINVAL:
289 db = dbopen(fname, flags, mode,
290 dbc->hashfirst ? DB_BTREE : DB_HASH,
291 dbc->hashfirst ? (void *) &bti : (void *) &hashi);
292 if (db != NULL)
293 dbc->hashfirst = !dbc->hashfirst;
294 default:
295 free(fname);
296 return db;
297 }
298 }
299
300 static krb5_error_code
krb5_db2_db_set_hashfirst(krb5_context context,int hashfirst)301 krb5_db2_db_set_hashfirst(krb5_context context, int hashfirst)
302 {
303 krb5_db2_context *dbc;
304 kdb5_dal_handle *dal_handle;
305
306 if (k5db2_inited(context))
307 return KRB5_KDB_DBNOTINITED;
308 dal_handle = (kdb5_dal_handle *) context->db_context;
309 dbc = (krb5_db2_context *) dal_handle->db_context;
310 dbc->hashfirst = hashfirst;
311 return 0;
312 }
313
314 /*
315 * initialization for data base routines.
316 */
317
318 krb5_error_code
krb5_db2_db_init(krb5_context context)319 krb5_db2_db_init(krb5_context context)
320 {
321 char *filename = NULL;
322 krb5_db2_context *db_ctx;
323 krb5_error_code retval;
324 kdb5_dal_handle *dal_handle;
325 char policy_db_name[1024], policy_lock_name[1024];
326
327 if (k5db2_inited(context))
328 return 0;
329
330 /* Check for presence of our context, if not present, allocate one. */
331 if ((retval = k5db2_init_context(context)))
332 return (retval);
333
334 dal_handle = (kdb5_dal_handle *) context->db_context;
335 db_ctx = dal_handle->db_context;
336 db_ctx->db = NULL;
337
338 if (!(filename = gen_dbsuffix(db_ctx->db_name, db_ctx->tempdb
339 ?KDB2_TEMP_LOCK_EXT:KDB2_LOCK_EXT)))
340 return ENOMEM;
341 db_ctx->db_lf_name = filename; /* so it gets freed by clear_context */
342
343 /*
344 * should be opened read/write so that write locking can work with
345 * POSIX systems
346 */
347 if ((db_ctx->db_lf_file = open(filename, O_RDWR, 0666)) < 0) {
348 if ((db_ctx->db_lf_file = open(filename, O_RDONLY, 0666)) < 0) {
349 retval = errno;
350
351 /* Solaris Kerberos: Better error logging */
352 (void) snprintf(errbuf, sizeof(errbuf), gettext("Failed to open \"%s\": "), filename);
353 krb5_db2_prepend_err_str(context, errbuf, retval, retval);
354
355 goto err_out;
356 }
357 }
358 db_ctx->db_inited++;
359
360 if ((retval = krb5_db2_db_get_age(context, NULL, &db_ctx->db_lf_time)))
361 goto err_out;
362
363 sprintf(policy_db_name, db_ctx->tempdb ? "%s~.kadm5" : "%s.kadm5",
364 db_ctx->db_name);
365 sprintf(policy_lock_name, "%s.lock", policy_db_name);
366
367 if ((retval = osa_adb_init_db(&db_ctx->policy_db, policy_db_name,
368 policy_lock_name, OSA_ADB_POLICY_DB_MAGIC)))
369 {
370 /* Solaris Kerberos: Better error logging */
371 snprintf(errbuf, sizeof(errbuf),
372 gettext("Failed to initialize db, \"%s\", lockfile, \"%s\" : "),
373 policy_db_name, policy_lock_name);
374 krb5_db2_prepend_err_str(context, errbuf, retval, retval);
375
376 goto err_out;
377 }
378 return 0;
379
380 err_out:
381 db_ctx->db = NULL;
382 k5db2_clear_context(db_ctx);
383 return (retval);
384 }
385
386 /*
387 * gracefully shut down database--must be called by ANY program that does
388 * a krb5_db2_db_init
389 */
390 krb5_error_code
krb5_db2_db_fini(krb5_context context)391 krb5_db2_db_fini(krb5_context context)
392 {
393 krb5_error_code retval = 0;
394 krb5_db2_context *db_ctx;
395 kdb5_dal_handle *dal_handle;
396
397 dal_handle = (kdb5_dal_handle *) context->db_context;
398 if (dal_handle == NULL) {
399 return 0;
400 }
401
402 db_ctx = (krb5_db2_context *) dal_handle->db_context;
403
404 if (k5db2_inited(context)) {
405 if (close(db_ctx->db_lf_file))
406 retval = errno;
407 else
408 retval = 0;
409 }
410 if (db_ctx) {
411 if (db_ctx->policy_db) {
412 retval =
413 osa_adb_fini_db(db_ctx->policy_db, OSA_ADB_POLICY_DB_MAGIC);
414 if (retval)
415 return retval;
416 }
417
418 k5db2_clear_context(db_ctx);
419 /* free(dal_handle->db_context); */
420 dal_handle->db_context = NULL;
421 }
422 return retval;
423 }
424
425 /*
426 * Set/Get the master key associated with the database
427 */
428 krb5_error_code
krb5_db2_db_set_mkey(krb5_context context,krb5_keyblock * key)429 krb5_db2_db_set_mkey(krb5_context context, krb5_keyblock *key)
430 {
431 krb5_db2_context *db_ctx;
432 kdb5_dal_handle *dal_handle;
433
434 if (!k5db2_inited(context))
435 return (KRB5_KDB_DBNOTINITED);
436
437 dal_handle = (kdb5_dal_handle *) context->db_context;
438 db_ctx = dal_handle->db_context;
439 db_ctx->db_master_key = key;
440 return 0;
441 }
442
443 krb5_error_code
krb5_db2_db_get_mkey(krb5_context context,krb5_keyblock ** key)444 krb5_db2_db_get_mkey(krb5_context context, krb5_keyblock **key)
445 {
446 krb5_db2_context *db_ctx;
447 kdb5_dal_handle *dal_handle;
448
449 if (!k5db2_inited(context))
450 return (KRB5_KDB_DBNOTINITED);
451
452 dal_handle = (kdb5_dal_handle *) context->db_context;
453 db_ctx = dal_handle->db_context;
454 *key = db_ctx->db_master_key;
455
456 return 0;
457 }
458
459 /*
460 * Set the "name" of the current database to some alternate value.
461 *
462 * Passing a null pointer as "name" will set back to the default.
463 * If the alternate database doesn't exist, nothing is changed.
464 *
465 * XXX rethink this
466 */
467
468 static krb5_error_code
krb5_db2_db_set_name(krb5_context context,char * name,int tempdb)469 krb5_db2_db_set_name(krb5_context context, char *name, int tempdb)
470 {
471 DB *db;
472 krb5_db2_context *db_ctx;
473 krb5_error_code kret;
474 kdb5_dal_handle *dal_handle;
475
476 if (k5db2_inited(context))
477 return KRB5_KDB_DBINITED;
478
479 /* Check for presence of our context, if not present, allocate one. */
480 if ((kret = k5db2_init_context(context)))
481 return (kret);
482
483 if (name == NULL)
484 name = default_db_name;
485
486 dal_handle = (kdb5_dal_handle *) context->db_context;
487 db_ctx = dal_handle->db_context;
488 db_ctx->tempdb = tempdb;
489 db = k5db2_dbopen(db_ctx, name, O_RDONLY, 0, tempdb);
490 if (db == NULL)
491 return errno;
492
493 db_ctx->db_name = strdup(name);
494 if (db_ctx->db_name == NULL) {
495 (*db->close) (db);
496 return ENOMEM;
497 }
498 (*db->close) (db);
499 return 0;
500 }
501
502 /*
503 * Return the last modification time of the database.
504 *
505 * Think about using fstat.
506 */
507
508 krb5_error_code
krb5_db2_db_get_age(krb5_context context,char * db_name,time_t * age)509 krb5_db2_db_get_age(krb5_context context, char *db_name, time_t *age)
510 {
511 krb5_db2_context *db_ctx;
512 kdb5_dal_handle *dal_handle;
513 struct stat st;
514
515 if (!k5db2_inited(context))
516 return (KRB5_KDB_DBNOTINITED);
517 dal_handle = (kdb5_dal_handle *) context->db_context;
518 db_ctx = (krb5_db2_context *) dal_handle->db_context;
519
520 if (fstat(db_ctx->db_lf_file, &st) < 0)
521 *age = -1;
522 else
523 *age = st.st_mtime;
524 return 0;
525 }
526
527 /*
528 * Remove the semaphore file; indicates that database is currently
529 * under renovation.
530 *
531 * This is only for use when moving the database out from underneath
532 * the server (for example, during slave updates).
533 */
534
535 static krb5_error_code
krb5_db2_db_start_update(krb5_context context)536 krb5_db2_db_start_update(krb5_context context)
537 {
538 return 0;
539 }
540
541 static krb5_error_code
krb5_db2_db_end_update(krb5_context context)542 krb5_db2_db_end_update(krb5_context context)
543 {
544 krb5_error_code retval;
545 krb5_db2_context *db_ctx;
546 kdb5_dal_handle *dal_handle;
547 struct stat st;
548 time_t now;
549 struct utimbuf utbuf;
550
551 if (!k5db2_inited(context))
552 return (KRB5_KDB_DBNOTINITED);
553
554 retval = 0;
555 dal_handle = (kdb5_dal_handle *) context->db_context;
556 db_ctx = dal_handle->db_context;
557 now = time((time_t *) NULL);
558 if (fstat(db_ctx->db_lf_file, &st) == 0) {
559 if (st.st_mtime >= now) {
560 utbuf.actime = st.st_mtime + 1;
561 utbuf.modtime = st.st_mtime + 1;
562 if (utime(db_ctx->db_lf_name, &utbuf))
563 retval = errno;
564 } else {
565 if (utime(db_ctx->db_lf_name, (struct utimbuf *) NULL))
566 retval = errno;
567 }
568 if (retval) {
569 /* Solaris Kerberos: Better error logging */
570 snprintf(errbuf, sizeof(errbuf), gettext("Failed to modify "
571 "access and modification times for \"%s\": "),
572 db_ctx->db_lf_name);
573 krb5_db2_prepend_err_str(context, errbuf, retval, retval);
574 }
575 } else {
576 retval = errno;
577 /* Solaris Kerberos: Better error logging */
578 snprintf(errbuf, sizeof(errbuf), gettext("Failed to stat \"%s\": "),
579 db_ctx->db_lf_name);
580 krb5_db2_prepend_err_str(context, errbuf, retval, retval);
581 }
582 if (!retval) {
583 if (fstat(db_ctx->db_lf_file, &st) == 0)
584 db_ctx->db_lf_time = st.st_mtime;
585 else {
586 retval = errno;
587 /* Solaris Kerberos: Better error logging */
588 snprintf(errbuf, sizeof(errbuf), gettext("Failed to stat \"%s\": "),
589 db_ctx->db_lf_name);
590 krb5_db2_prepend_err_str(context, errbuf, retval, retval);
591 }
592 }
593 return (retval);
594 }
595
596 #define MAX_LOCK_TRIES 5
597
598 krb5_error_code
krb5_db2_db_lock(krb5_context context,int in_mode)599 krb5_db2_db_lock(krb5_context context, int in_mode)
600 {
601 krb5_db2_context *db_ctx;
602 int krb5_lock_mode;
603 DB *db;
604 krb5_error_code retval;
605 time_t mod_time;
606 kdb5_dal_handle *dal_handle;
607 int mode, gotlock, tries;
608
609 switch (in_mode) {
610 case KRB5_DB_LOCKMODE_PERMANENT:
611 mode = KRB5_DB_LOCKMODE_EXCLUSIVE;
612 break;
613 case KRB5_DB_LOCKMODE_EXCLUSIVE:
614 mode = KRB5_LOCKMODE_EXCLUSIVE;
615 break;
616
617 case KRB5_DB_LOCKMODE_SHARED:
618 mode = KRB5_LOCKMODE_SHARED;
619 break;
620 default:
621 return EINVAL;
622 }
623
624 if (!k5db2_inited(context))
625 return KRB5_KDB_DBNOTINITED;
626
627 dal_handle = (kdb5_dal_handle *) context->db_context;
628 db_ctx = (krb5_db2_context *) dal_handle->db_context;
629 if (db_ctx->db_locks_held && (db_ctx->db_lock_mode >= mode)) {
630 /* No need to upgrade lock, just return */
631 db_ctx->db_locks_held++;
632 goto policy_lock;
633 }
634
635 if ((mode != KRB5_LOCKMODE_SHARED) && (mode != KRB5_LOCKMODE_EXCLUSIVE))
636 return KRB5_KDB_BADLOCKMODE;
637
638 krb5_lock_mode = mode | KRB5_LOCKMODE_DONTBLOCK;
639 for (gotlock = tries = 0; tries < MAX_LOCK_TRIES; tries++) {
640 retval = krb5_lock_file(context, db_ctx->db_lf_file, krb5_lock_mode);
641 if (retval == 0) {
642 gotlock++;
643 break;
644 } else if (retval == EBADF && mode == KRB5_DB_LOCKMODE_EXCLUSIVE) {
645 /* tried to exclusive-lock something we don't have */
646 /* write access to */
647
648 /* Solaris Kerberos: Better error logging */
649 snprintf(errbuf, sizeof(errbuf),
650 gettext("Failed to exclusively lock \"%s\": "),
651 db_ctx->db_lf_name);
652 krb5_db2_prepend_err_str(context, errbuf, EBADF, EBADF);
653
654 return KRB5_KDB_CANTLOCK_DB;
655 }
656 sleep(1);
657 }
658
659 if (retval) {
660 /* Solaris Kerberos: Better error logging */
661 snprintf(errbuf, sizeof(errbuf),
662 gettext("Failed to lock \"%s\": "),
663 db_ctx->db_lf_name);
664 krb5_db2_prepend_err_str(context, errbuf, retval, retval);
665 }
666
667 if (retval == EACCES)
668 return KRB5_KDB_CANTLOCK_DB;
669 else if (retval == EAGAIN || retval == EWOULDBLOCK)
670 return OSA_ADB_CANTLOCK_DB;
671 else if (retval != 0)
672 return retval;
673
674 if ((retval = krb5_db2_db_get_age(context, NULL, &mod_time)))
675 goto lock_error;
676
677 db = k5db2_dbopen(db_ctx, db_ctx->db_name,
678 mode == KRB5_LOCKMODE_SHARED ? O_RDONLY : O_RDWR, 0600, db_ctx->tempdb);
679 if (db) {
680 db_ctx->db_lf_time = mod_time;
681 db_ctx->db = db;
682 } else {
683 retval = errno;
684
685 /* Solaris Kerberos: Better error logging */
686 snprintf(errbuf, sizeof(errbuf),
687 gettext("Failed to open db \"%s\": "),
688 db_ctx->db_name);
689 krb5_db2_prepend_err_str(context, errbuf, retval, retval);
690
691 db_ctx->db = NULL;
692 goto lock_error;
693 }
694
695 db_ctx->db_lock_mode = mode;
696 db_ctx->db_locks_held++;
697
698 policy_lock:
699 if ((retval = osa_adb_get_lock(db_ctx->policy_db, in_mode))) {
700 krb5_db2_db_unlock(context);
701 }
702 return retval;
703
704 lock_error:;
705 db_ctx->db_lock_mode = 0;
706 db_ctx->db_locks_held = 0;
707 krb5_db2_db_unlock(context);
708 return retval;
709 }
710
711 krb5_error_code
krb5_db2_db_unlock(krb5_context context)712 krb5_db2_db_unlock(krb5_context context)
713 {
714 krb5_db2_context *db_ctx;
715 kdb5_dal_handle *dal_handle;
716 DB *db;
717 krb5_error_code retval;
718
719 if (!k5db2_inited(context))
720 return KRB5_KDB_DBNOTINITED;
721
722 dal_handle = (kdb5_dal_handle *) context->db_context;
723 db_ctx = (krb5_db2_context *) dal_handle->db_context;
724
725 if ((retval = osa_adb_release_lock(db_ctx->policy_db))) {
726 return retval;
727 }
728
729 if (!db_ctx->db_locks_held) /* lock already unlocked */
730 return KRB5_KDB_NOTLOCKED;
731 db = db_ctx->db;
732 if (--(db_ctx->db_locks_held) == 0) {
733 (*db->close) (db);
734 db_ctx->db = NULL;
735
736 retval = krb5_lock_file(context, db_ctx->db_lf_file,
737 KRB5_LOCKMODE_UNLOCK);
738 db_ctx->db_lock_mode = 0;
739 return (retval);
740 }
741 return 0;
742 }
743
744 /*
745 * Create the database, assuming it's not there.
746 */
747 krb5_error_code
krb5_db2_db_create(krb5_context context,char * db_name,krb5_int32 flags)748 krb5_db2_db_create(krb5_context context, char *db_name, krb5_int32 flags)
749 {
750 register krb5_error_code retval = 0;
751 kdb5_dal_handle *dal_handle;
752 char *okname;
753 char *db_name2 = NULL;
754 int fd;
755 krb5_db2_context *db_ctx;
756 DB *db;
757 char policy_db_name[1024], policy_lock_name[1024];
758
759 if ((retval = k5db2_init_context(context)))
760 return (retval);
761
762 dal_handle = (kdb5_dal_handle *) context->db_context;
763 db_ctx = (krb5_db2_context *) dal_handle->db_context;
764 switch (flags) {
765 case KRB5_KDB_CREATE_HASH:
766 if ((retval = krb5_db2_db_set_hashfirst(context, TRUE)))
767 return retval;
768 break;
769 case KRB5_KDB_CREATE_BTREE:
770 case 0:
771 if ((retval = krb5_db2_db_set_hashfirst(context, FALSE)))
772 return retval;
773 break;
774 default:
775 return KRB5_KDB_BAD_CREATEFLAGS;
776 }
777 db = k5db2_dbopen(db_ctx, db_name, O_RDWR | O_CREAT | O_EXCL, 0600, db_ctx->tempdb);
778 if (db == NULL) {
779 retval = errno;
780
781 /* Solaris Kerberos: Better error logging */
782 snprintf(errbuf, sizeof(errbuf), gettext("Failed to open \"%s\": "), db_name);
783 krb5_db2_prepend_err_str(context, errbuf, retval, retval);
784 }
785 else
786 (*db->close) (db);
787 if (retval == 0) {
788
789 db_name2 = db_ctx->tempdb ? gen_dbsuffix(db_name, "~") : strdup(db_name);
790 if (db_name2 == NULL)
791 return ENOMEM;
792 okname = gen_dbsuffix(db_name2, KDB2_LOCK_EXT);
793 if (!okname)
794 retval = ENOMEM;
795 else {
796 fd = open(okname, O_CREAT | O_RDWR | O_TRUNC, 0600);
797 if (fd < 0) {
798 retval = errno;
799 /* Solaris Kerberos: Better error logging */
800 snprintf(errbuf, sizeof(errbuf), gettext("Failed to open \"%s\": "), okname);
801 krb5_db2_prepend_err_str(context, errbuf, retval, retval);
802 }
803 else
804 close(fd);
805 free_dbsuffix(okname);
806 }
807 }
808
809 sprintf(policy_db_name, "%s.kadm5", db_name2);
810 sprintf(policy_lock_name, "%s.lock", policy_db_name);
811
812 retval = osa_adb_create_db(policy_db_name,
813 policy_lock_name, OSA_ADB_POLICY_DB_MAGIC);
814 free(db_name2);
815 return retval;
816 }
817
818 /*
819 * Destroy the database. Zero's out all of the files, just to be sure.
820 */
821 static krb5_error_code
destroy_file_suffix(char * dbname,char * suffix)822 destroy_file_suffix(char *dbname, char *suffix)
823 {
824 char *filename;
825 struct stat statb;
826 int nb, fd;
827 unsigned int j;
828 off_t pos;
829 char buf[BUFSIZ];
830 char zbuf[BUFSIZ];
831 int dowrite;
832
833 filename = gen_dbsuffix(dbname, suffix);
834 if (filename == 0)
835 return ENOMEM;
836 if ((fd = open(filename, O_RDWR, 0)) < 0) {
837 free(filename);
838 return errno;
839 }
840 /* fstat() will probably not fail unless using a remote filesystem
841 * (which is inappropriate for the kerberos database) so this check
842 * is mostly paranoia. */
843 if (fstat(fd, &statb) == -1) {
844 int retval = errno;
845 free(filename);
846 return retval;
847 }
848 /*
849 * Stroll through the file, reading in BUFSIZ chunks. If everything
850 * is zero, then we're done for that block, otherwise, zero the block.
851 * We would like to just blast through everything, but some DB
852 * implementations make holey files and writing data to the holes
853 * causes actual blocks to be allocated which is no good, since
854 * we're just about to unlink it anyways.
855 */
856 memset(zbuf, 0, BUFSIZ);
857 pos = 0;
858 while (pos < statb.st_size) {
859 dowrite = 0;
860 nb = read(fd, buf, BUFSIZ);
861 if (nb < 0) {
862 int retval = errno;
863 free(filename);
864 return retval;
865 }
866 for (j = 0; j < nb; j++) {
867 if (buf[j] != '\0') {
868 dowrite = 1;
869 break;
870 }
871 }
872 /* For signedness */
873 j = nb;
874 if (dowrite) {
875 lseek(fd, pos, SEEK_SET);
876 nb = write(fd, zbuf, j);
877 if (nb < 0) {
878 int retval = errno;
879 free(filename);
880 return retval;
881 }
882 }
883 pos += nb;
884 }
885 /* ??? Is fsync really needed? I don't know of any non-networked
886 * filesystem which will discard queued writes to disk if a file
887 * is deleted after it is closed. --jfc */
888 #ifndef NOFSYNC
889 fsync(fd);
890 #endif
891 close(fd);
892
893 if (unlink(filename)) {
894 free(filename);
895 return (errno);
896 }
897 free(filename);
898 return (0);
899 }
900
901 /*
902 * Since the destroy operation happens outside the init/fini bracket, we
903 * have some tomfoolery to undergo here. If we're operating under no
904 * database context, then we initialize with the default. If the caller
905 * wishes a different context (e.g. different dispatch table), it's their
906 * responsibility to call kdb5_db_set_dbops() before this call. That will
907 * set up the right dispatch table values (e.g. name extensions).
908 *
909 * Not quite valid due to ripping out of dbops...
910 */
911 krb5_error_code
krb5_db2_db_destroy(krb5_context context,char * dbname)912 krb5_db2_db_destroy(krb5_context context, char *dbname)
913 {
914 krb5_error_code retval1, retval2;
915 krb5_boolean tmpcontext;
916 char policy_db_name[1024], policy_lock_name[1024];
917
918 tmpcontext = 0;
919 if (!context->db_context
920 || !((kdb5_dal_handle *) context->db_context)->db_context) {
921 tmpcontext = 1;
922 if ((retval1 = k5db2_init_context(context)))
923 return (retval1);
924 }
925
926 retval1 = retval2 = 0;
927 retval1 = destroy_file_suffix(dbname, "");
928 retval2 = destroy_file_suffix(dbname, KDB2_LOCK_EXT);
929
930 if (tmpcontext) {
931 k5db2_clear_context((krb5_db2_context *) ((kdb5_dal_handle *) context->
932 db_context)->db_context);
933 free(((kdb5_dal_handle *) context->db_context)->db_context);
934 ((kdb5_dal_handle *) context->db_context)->db_context = NULL;
935 }
936
937 if (retval1 || retval2)
938 return (retval1 ? retval1 : retval2);
939
940 assert (strlen(dbname) + strlen("%s.kadm5") < sizeof(policy_db_name));
941 sprintf(policy_db_name, "%s.kadm5", dbname);
942 /* XXX finish this */
943 sprintf(policy_lock_name, "%s.lock", policy_db_name);
944
945 retval1 = osa_adb_destroy_db(policy_db_name,
946 policy_lock_name, OSA_ADB_POLICY_DB_MAGIC);
947
948 return retval1;
949 }
950
951 /*
952 * look up a principal in the data base.
953 * returns number of entries found, and whether there were
954 * more than requested.
955 */
956
957 krb5_error_code
krb5_db2_db_get_principal(krb5_context context,krb5_const_principal searchfor,krb5_db_entry * entries,int * nentries,krb5_boolean * more)958 krb5_db2_db_get_principal(krb5_context context,
959 krb5_const_principal searchfor,
960 krb5_db_entry *entries, /* filled in */
961 int *nentries, /* how much room/how many found */
962 krb5_boolean *more) /* are there more? */
963 {
964 krb5_db2_context *db_ctx;
965 krb5_error_code retval;
966 DB *db;
967 DBT key, contents;
968 krb5_data keydata, contdata;
969 int trynum, dbret;
970 kdb5_dal_handle *dal_handle;
971
972 *more = FALSE;
973 *nentries = 0;
974
975 if (!k5db2_inited(context))
976 return KRB5_KDB_DBNOTINITED;
977
978 dal_handle = (kdb5_dal_handle *) context->db_context;
979 db_ctx = (krb5_db2_context *) dal_handle->db_context;
980
981 for (trynum = 0; trynum < KRB5_DB2_MAX_RETRY; trynum++) {
982 if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_SHARED))) {
983 if (db_ctx->db_nb_locks)
984 return (retval);
985 sleep(1);
986 continue;
987 }
988 break;
989 }
990 if (trynum == KRB5_DB2_MAX_RETRY)
991 return KRB5_KDB_DB_INUSE;
992
993 /* XXX deal with wildcard lookups */
994 retval = krb5_encode_princ_dbkey(context, &keydata, searchfor);
995 if (retval)
996 goto cleanup;
997 key.data = keydata.data;
998 key.size = keydata.length;
999
1000 db = db_ctx->db;
1001 dbret = (*db->get) (db, &key, &contents, 0);
1002 retval = errno;
1003 krb5_free_data_contents(context, &keydata);
1004 switch (dbret) {
1005 case 1:
1006 retval = 0;
1007 /*LINTED*/
1008 case -1:
1009 default:
1010 *nentries = 0;
1011 goto cleanup;
1012 case 0:
1013 contdata.data = contents.data;
1014 contdata.length = contents.size;
1015 retval = krb5_decode_princ_contents(context, &contdata, entries);
1016 if (!retval)
1017 *nentries = 1;
1018 break;
1019 }
1020
1021 cleanup:
1022 (void) krb5_db2_db_unlock(context); /* unlock read lock */
1023 return retval;
1024 }
1025
1026 /*
1027 Free stuff returned by krb5_db2_db_get_principal.
1028 */
1029 krb5_error_code
krb5_db2_db_free_principal(krb5_context context,krb5_db_entry * entries,int nentries)1030 krb5_db2_db_free_principal(krb5_context context, krb5_db_entry *entries,
1031 int nentries)
1032 {
1033 register int i;
1034 for (i = 0; i < nentries; i++)
1035 krb5_dbe_free_contents(context, &entries[i]);
1036 return 0;
1037 }
1038
1039 /*
1040 Stores the *"nentries" entry structures pointed to by "entries" in the
1041 database.
1042
1043 *"nentries" is updated upon return to reflect the number of records
1044 acutally stored; the first *"nstored" records will have been stored in the
1045 database (even if an error occurs).
1046
1047 */
1048
1049 krb5_error_code
krb5_db2_db_put_principal(krb5_context context,krb5_db_entry * entries,int * nentries,char ** db_args)1050 krb5_db2_db_put_principal(krb5_context context,
1051 krb5_db_entry *entries,
1052 int *nentries, /* number of entry structs to update */
1053 char **db_args)
1054 {
1055 int i, n, dbret;
1056 DB *db;
1057 DBT key, contents;
1058 krb5_data contdata, keydata;
1059 krb5_error_code retval;
1060 krb5_db2_context *db_ctx;
1061 kdb5_dal_handle *dal_handle;
1062 kdb_incr_update_t *upd, *fupd;
1063 char *princ_name = NULL;
1064 kdb_log_context *log_ctx;
1065
1066 krb5_clear_error_message (context);
1067 if (db_args) {
1068 /* DB2 does not support db_args DB arguments for principal */
1069 krb5_set_error_message(context, EINVAL,
1070 gettext("Unsupported argument \"%s\" for db2"),
1071 db_args[0]);
1072 return EINVAL;
1073 }
1074
1075 log_ctx = context->kdblog_context;
1076
1077 n = *nentries;
1078 *nentries = 0;
1079 if (!k5db2_inited(context))
1080 return KRB5_KDB_DBNOTINITED;
1081
1082 dal_handle = (kdb5_dal_handle *) context->db_context;
1083 db_ctx = (krb5_db2_context *) dal_handle->db_context;
1084 if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE)))
1085 return retval;
1086
1087 /*
1088 * Solaris Kerberos: We need the lock since ulog_conv_2logentry() does a get
1089 */
1090 if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
1091 if (!(upd = (kdb_incr_update_t *)
1092 malloc(sizeof (kdb_incr_update_t)*n))) {
1093 retval = errno;
1094 goto err_lock;
1095 }
1096 fupd = upd;
1097
1098 (void) memset(upd, 0, sizeof(kdb_incr_update_t)*n);
1099
1100 if ((retval = ulog_conv_2logentry(context, entries, upd, n))) {
1101 goto err_lock;
1102 }
1103 }
1104
1105 db = db_ctx->db;
1106 if ((retval = krb5_db2_db_start_update(context))) {
1107 (void) krb5_db2_db_unlock(context);
1108 goto err_lock;
1109 }
1110
1111 /* for each one, stuff temps, and do replace/append */
1112 for (i = 0; i < n; i++) {
1113 /*
1114 * Solaris Kerberos: We'll be sharing the same locks as db for logging
1115 */
1116 if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
1117 if ((retval = krb5_unparse_name(context, entries->princ,
1118 &princ_name)))
1119 goto err_lock;
1120
1121 upd->kdb_princ_name.utf8str_t_val = princ_name;
1122 upd->kdb_princ_name.utf8str_t_len = strlen(princ_name);
1123
1124 if (retval = ulog_add_update(context, upd))
1125 goto err_lock;
1126 }
1127
1128 retval = krb5_encode_princ_contents(context, &contdata, entries);
1129 if (retval)
1130 break;
1131 contents.data = contdata.data;
1132 contents.size = contdata.length;
1133 retval = krb5_encode_princ_dbkey(context, &keydata, entries->princ);
1134 if (retval) {
1135 krb5_free_data_contents(context, &contdata);
1136 break;
1137 }
1138
1139 key.data = keydata.data;
1140 key.size = keydata.length;
1141 dbret = (*db->put) (db, &key, &contents, 0);
1142 retval = dbret ? errno : 0;
1143 krb5_free_data_contents(context, &keydata);
1144 krb5_free_data_contents(context, &contdata);
1145 if (retval)
1146 break;
1147 else if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
1148 /*
1149 * We need to make sure the db record is synced before we mark
1150 * it as committed via finish_update.
1151 */
1152 dbret = (*db->sync)(db, 0);
1153 if (dbret) {
1154 retval = errno;
1155 goto err_lock;
1156 }
1157 (void) ulog_finish_update(context, upd);
1158 upd++;
1159 }
1160 entries++; /* bump to next struct */
1161 }
1162
1163 (void) krb5_db2_db_end_update(context);
1164
1165 err_lock:
1166 (void) krb5_db2_db_unlock(context); /* unlock database */
1167
1168 if (log_ctx && (log_ctx->iproprole == IPROP_MASTER))
1169 ulog_free_entries(fupd, n);
1170
1171 *nentries = i;
1172 return (retval);
1173 }
1174
1175 /*
1176 * delete a principal from the data base.
1177 * returns number of entries removed
1178 */
1179
1180 krb5_error_code
krb5_db2_db_delete_principal(krb5_context context,krb5_const_principal searchfor,int * nentries)1181 krb5_db2_db_delete_principal(krb5_context context,
1182 krb5_const_principal searchfor,
1183 int *nentries) /* how many found & deleted */
1184 {
1185 krb5_error_code retval;
1186 krb5_db_entry entry;
1187 krb5_db2_context *db_ctx;
1188 DB *db;
1189 DBT key, contents;
1190 krb5_data keydata, contdata;
1191 int i, dbret;
1192 kdb5_dal_handle *dal_handle;
1193 kdb_incr_update_t upd;
1194 char *princ_name = NULL;
1195 kdb_log_context *log_ctx;
1196
1197 log_ctx = context->kdblog_context;
1198
1199 if (!k5db2_inited(context))
1200 return KRB5_KDB_DBNOTINITED;
1201
1202 dal_handle = (kdb5_dal_handle *) context->db_context;
1203 db_ctx = (krb5_db2_context *) dal_handle->db_context;
1204 if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE)))
1205 return (retval);
1206
1207 if ((retval = krb5_db2_db_start_update(context))) {
1208 (void) krb5_db2_db_unlock(context); /* unlock write lock */
1209 return (retval);
1210 }
1211
1212 if ((retval = krb5_encode_princ_dbkey(context, &keydata, searchfor)))
1213 goto cleanup;
1214 key.data = keydata.data;
1215 key.size = keydata.length;
1216
1217 db = db_ctx->db;
1218 dbret = (*db->get) (db, &key, &contents, 0);
1219 retval = errno;
1220 switch (dbret) {
1221 case 1:
1222 retval = KRB5_KDB_NOENTRY;
1223 /*LINTED*/
1224 case -1:
1225 default:
1226 *nentries = 0;
1227 goto cleankey;
1228 case 0:
1229 ;
1230 }
1231 /*
1232 * Solaris Kerberos: We'll be sharing the same locks as db for logging
1233 */
1234 if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
1235 if ((retval = krb5_unparse_name(context, searchfor, &princ_name))) {
1236 (void) krb5_db2_db_unlock(context);
1237 return retval;
1238 }
1239
1240 (void) memset(&upd, 0, sizeof (kdb_incr_update_t));
1241
1242 upd.kdb_princ_name.utf8str_t_val = princ_name;
1243 upd.kdb_princ_name.utf8str_t_len = strlen(princ_name);
1244
1245 if (retval = ulog_delete_update(context, &upd)) {
1246 free(princ_name);
1247 (void) krb5_db2_db_unlock(context);
1248 return retval;
1249 }
1250
1251 free(princ_name);
1252 }
1253
1254 memset((char *) &entry, 0, sizeof(entry));
1255 contdata.data = contents.data;
1256 contdata.length = contents.size;
1257 retval = krb5_decode_princ_contents(context, &contdata, &entry);
1258 if (retval)
1259 goto cleankey;
1260 *nentries = 1;
1261
1262 /* Clear encrypted key contents */
1263 for (i = 0; i < entry.n_key_data; i++) {
1264 if (entry.key_data[i].key_data_length[0]) {
1265 memset((char *) entry.key_data[i].key_data_contents[0], 0,
1266 (unsigned) entry.key_data[i].key_data_length[0]);
1267 }
1268 }
1269
1270 retval = krb5_encode_princ_contents(context, &contdata, &entry);
1271 krb5_dbe_free_contents(context, &entry);
1272 if (retval)
1273 goto cleankey;
1274
1275 contents.data = contdata.data;
1276 contents.size = contdata.length;
1277 dbret = (*db->put) (db, &key, &contents, 0);
1278 retval = dbret ? errno : 0;
1279 krb5_free_data_contents(context, &contdata);
1280 if (retval)
1281 goto cleankey;
1282 dbret = (*db->del) (db, &key, 0);
1283 retval = dbret ? errno : 0;
1284
1285 /*
1286 * We need to commit our update upon success
1287 */
1288 if (!retval)
1289 if (log_ctx && (log_ctx->iproprole == IPROP_MASTER))
1290 (void) ulog_finish_update(context, &upd);
1291
1292 cleankey:
1293 krb5_free_data_contents(context, &keydata);
1294
1295 cleanup:
1296 (void) krb5_db2_db_end_update(context);
1297 (void) krb5_db2_db_unlock(context); /* unlock write lock */
1298 return retval;
1299 }
1300
1301 krb5_error_code
krb5_db2_db_iterate_ext(krb5_context context,krb5_error_code (* func)(krb5_pointer,krb5_db_entry *),krb5_pointer func_arg,int backwards,int recursive)1302 krb5_db2_db_iterate_ext(krb5_context context,
1303 krb5_error_code(*func) (krb5_pointer, krb5_db_entry *),
1304 krb5_pointer func_arg,
1305 int backwards, int recursive)
1306 {
1307 krb5_db2_context *db_ctx;
1308 DB *db;
1309 DBT key, contents;
1310 krb5_data contdata;
1311 krb5_db_entry entries;
1312 krb5_error_code retval;
1313 kdb5_dal_handle *dal_handle;
1314 int dbret;
1315 void *cookie;
1316
1317 cookie = NULL;
1318 if (!k5db2_inited(context))
1319 return KRB5_KDB_DBNOTINITED;
1320
1321 dal_handle = (kdb5_dal_handle *) context->db_context;
1322 db_ctx = (krb5_db2_context *) dal_handle->db_context;
1323 retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_SHARED);
1324
1325 if (retval)
1326 return retval;
1327
1328 db = db_ctx->db;
1329 if (recursive && db->type != DB_BTREE) {
1330 (void) krb5_db2_db_unlock(context);
1331 return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */
1332 }
1333
1334 if (!recursive) {
1335 dbret = (*db->seq) (db, &key, &contents, backwards ? R_LAST : R_FIRST);
1336 } else {
1337 #ifdef HAVE_BT_RSEQ
1338 dbret = bt_rseq(db, &key, &contents, &cookie,
1339 backwards ? R_LAST : R_FIRST);
1340 #else
1341 (void) krb5_db2_db_unlock(context);
1342 return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */
1343 #endif
1344 }
1345 while (dbret == 0) {
1346 contdata.data = contents.data;
1347 contdata.length = contents.size;
1348 retval = krb5_decode_princ_contents(context, &contdata, &entries);
1349 if (retval)
1350 break;
1351 retval = (*func) (func_arg, &entries);
1352 krb5_dbe_free_contents(context, &entries);
1353 if (retval)
1354 break;
1355 if (!recursive) {
1356 dbret = (*db->seq) (db, &key, &contents,
1357 backwards ? R_PREV : R_NEXT);
1358 } else {
1359 #ifdef HAVE_BT_RSEQ
1360 dbret = bt_rseq(db, &key, &contents, &cookie,
1361 backwards ? R_PREV : R_NEXT);
1362 #else
1363 (void) krb5_db2_db_unlock(context);
1364 return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */
1365 #endif
1366 }
1367 }
1368 switch (dbret) {
1369 case 1:
1370 case 0:
1371 break;
1372 case -1:
1373 default:
1374 retval = errno;
1375 }
1376 (void) krb5_db2_db_unlock(context);
1377 return retval;
1378 }
1379
1380 krb5_error_code
krb5_db2_db_iterate(krb5_context context,char * match_expr,krb5_error_code (* func)(krb5_pointer,krb5_db_entry *),krb5_pointer func_arg,char ** db_args)1381 krb5_db2_db_iterate(krb5_context context,
1382 char *match_expr,
1383 krb5_error_code(*func) (krb5_pointer, krb5_db_entry *),
1384 krb5_pointer func_arg, char **db_args)
1385 {
1386 char **t_ptr = db_args;
1387 int backwards = 0, recursive = 0;
1388
1389 while (t_ptr && *t_ptr) {
1390 char *opt = NULL, *val = NULL;
1391
1392 krb5_db2_get_db_opt(*t_ptr, &opt, &val);
1393
1394 /* Solaris Kerberos: adding support for -rev/recurse flags */
1395 if (val && !strcmp(val, "rev"))
1396 backwards = 1;
1397 else if (val && !strcmp(val, "recurse"))
1398 recursive = 1;
1399 else {
1400 krb5_set_error_message(context, EINVAL,
1401 gettext("Unsupported argument \"%s\" for db2"),
1402 val);
1403 free(opt);
1404 free(val);
1405 return EINVAL;
1406 }
1407
1408 free(opt);
1409 free(val);
1410 t_ptr++;
1411 }
1412
1413 /* Solaris Kerberos: adding support for -rev/recurse flags */
1414 return krb5_db2_db_iterate_ext(context, func, func_arg, backwards, recursive);
1415 }
1416
1417 krb5_boolean
krb5_db2_db_set_lockmode(krb5_context context,krb5_boolean mode)1418 krb5_db2_db_set_lockmode(krb5_context context, krb5_boolean mode)
1419 {
1420 krb5_boolean old;
1421 krb5_db2_context *db_ctx;
1422 kdb5_dal_handle *dal_handle;
1423
1424 dal_handle = (kdb5_dal_handle *) context->db_context;
1425 old = mode;
1426 if (dal_handle && (db_ctx = (krb5_db2_context *) dal_handle->db_context)) {
1427 old = db_ctx->db_nb_locks;
1428 db_ctx->db_nb_locks = mode;
1429 }
1430 return old;
1431 }
1432
1433 /*
1434 * DAL API functions
1435 */
1436 krb5_error_code
krb5_db2_lib_init()1437 krb5_db2_lib_init()
1438 {
1439 return 0;
1440 }
1441
1442 krb5_error_code
krb5_db2_lib_cleanup()1443 krb5_db2_lib_cleanup()
1444 {
1445 /* right now, no cleanup required */
1446 return 0;
1447 }
1448
1449 krb5_error_code
krb5_db2_open(krb5_context kcontext,char * conf_section,char ** db_args,int mode)1450 krb5_db2_open(krb5_context kcontext,
1451 char *conf_section, char **db_args, int mode)
1452 {
1453 krb5_error_code status = 0;
1454 char **t_ptr = db_args;
1455 int db_name_set = 0, tempdb=0;
1456 char *dbname = NULL;
1457
1458 krb5_clear_error_message (kcontext);
1459
1460 if (k5db2_inited(kcontext))
1461 return 0;
1462
1463 while (t_ptr && *t_ptr) {
1464 char *opt = NULL, *val = NULL;
1465
1466 krb5_db2_get_db_opt(*t_ptr, &opt, &val);
1467 if (opt && !strcmp(opt, "dbname")) {
1468 if (dbname) free(dbname);
1469 dbname = strdup(val);
1470 }
1471 else if (!opt && !strcmp(val, "temporary") ) {
1472 tempdb = 1;
1473 }
1474 /* ignore hash argument. Might have been passed from create */
1475 else if (!opt || strcmp(opt, "hash")) {
1476 krb5_set_error_message(kcontext, EINVAL,
1477 gettext("Unsupported argument \"%s\" for db2"),
1478 opt ? opt : val);
1479 free(opt);
1480 free(val);
1481 return EINVAL;
1482 }
1483
1484 free(opt);
1485 free(val);
1486 t_ptr++;
1487 }
1488
1489 if(dbname) {
1490 status = krb5_db2_db_set_name(kcontext, dbname, tempdb);
1491 free(dbname);
1492 if (status) {
1493 /* Solaris Kerberos: Better error logging */
1494 snprintf(errbuf, sizeof(errbuf), gettext("Failed to set db2 name to \"%s\": "), dbname);
1495 krb5_db2_prepend_err_str(kcontext, errbuf, status, status);
1496
1497 goto clean_n_exit;
1498 }
1499 db_name_set = 1;
1500 }
1501 if (!db_name_set) {
1502 char *value = NULL;
1503 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME, /* under given conf section */
1504 NULL, &value);
1505
1506 if (value == NULL) {
1507 /* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */
1508 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME, /* under given realm */
1509 default_db_name, &value);
1510
1511 if (status) {
1512 /* Solaris Kerberos: Better error logging */
1513 snprintf(errbuf, sizeof(errbuf), gettext("Failed when searching for "
1514 "\"%s\", \"%s\", \"%s\" in profile: "), KDB_REALM_SECTION,
1515 KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME);
1516 krb5_db2_prepend_err_str(kcontext, errbuf, status, status);
1517
1518 goto clean_n_exit;
1519 }
1520 }
1521
1522 status = krb5_db2_db_set_name(kcontext, value, tempdb);
1523
1524 if (status) {
1525
1526 /* Solaris Kerberos: Better error logging */
1527 snprintf(errbuf, sizeof(errbuf), gettext("Failed to set db2 name to \"%s\": "), value);
1528 krb5_db2_prepend_err_str(kcontext, errbuf, status, status);
1529 profile_release_string(value);
1530 goto clean_n_exit;
1531 }
1532 profile_release_string(value);
1533
1534 }
1535
1536 status = krb5_db2_db_init(kcontext);
1537 if (status) {
1538 /* Solaris Kerberos: Better error logging */
1539 snprintf(errbuf, sizeof(errbuf), gettext("Failed to initialize db2 db: "));
1540 krb5_db2_prepend_err_str(kcontext, errbuf, status, status);
1541 }
1542
1543 clean_n_exit:
1544 return status;
1545 }
1546
1547 krb5_error_code
krb5_db2_create(krb5_context kcontext,char * conf_section,char ** db_args)1548 krb5_db2_create(krb5_context kcontext, char *conf_section, char **db_args)
1549 {
1550 krb5_error_code status = 0;
1551 char **t_ptr = db_args;
1552 int db_name_set = 0, tempdb=0;
1553 krb5_int32 flags = KRB5_KDB_CREATE_BTREE;
1554 char *db_name = NULL;
1555
1556 krb5_clear_error_message (kcontext);
1557
1558 if (k5db2_inited(kcontext))
1559 return 0;
1560
1561 while (t_ptr && *t_ptr) {
1562 char *opt = NULL, *val = NULL;
1563
1564 krb5_db2_get_db_opt(*t_ptr, &opt, &val);
1565 if (opt && !strcmp(opt, "dbname")) {
1566 db_name = strdup(val);
1567 if (db_name == NULL)
1568 return ENOMEM;
1569 }
1570 else if (!opt && !strcmp(val, "temporary")) {
1571 tempdb = 1;
1572 }
1573 else if (opt && !strcmp(opt, "hash")) {
1574 flags = KRB5_KDB_CREATE_HASH;
1575 } else {
1576 krb5_set_error_message(kcontext, EINVAL,
1577 gettext("Unsupported argument \"%s\" for db2"),
1578 opt ? opt : val);
1579 free(opt);
1580 free(val);
1581 return EINVAL;
1582 }
1583
1584 free(opt);
1585 free(val);
1586 t_ptr++;
1587 }
1588 if (db_name) {
1589 status = krb5_db2_db_set_name(kcontext, db_name, tempdb);
1590 if (!status) {
1591 status = EEXIST;
1592 goto clean_n_exit;
1593 }
1594 db_name_set = 1;
1595 }
1596 if (!db_name_set) {
1597 char *value = NULL;
1598 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext),
1599 KDB_MODULE_SECTION, conf_section,
1600 /* under given conf section */
1601 KDB_DB2_DATABASE_NAME, NULL, &value);
1602
1603 if (value == NULL) {
1604 /* Special case for db2. We might actually be looking at
1605 * old type config file where database is specified as
1606 * part of realm. */
1607 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext),
1608 KDB_REALM_SECTION,
1609 KRB5_DB_GET_REALM(kcontext),
1610 /* under given realm */
1611 KDB_DB2_DATABASE_NAME,
1612 default_db_name, &value);
1613 if (status) {
1614 goto clean_n_exit;
1615 }
1616 }
1617
1618 db_name = strdup(value);
1619 /* Solaris Kerberos: for safety */
1620 if (db_name == NULL) {
1621 status = ENOMEM;
1622 goto clean_n_exit;
1623 }
1624 status = krb5_db2_db_set_name(kcontext, value, tempdb);
1625 profile_release_string(value);
1626 if (!status) {
1627 status = EEXIST;
1628 goto clean_n_exit;
1629 }
1630
1631 }
1632
1633 status = krb5_db2_db_create(kcontext, db_name, flags);
1634 if (status)
1635 goto clean_n_exit;
1636 /* db2 has a problem of needing to close and open the database again. This removes that need */
1637 status = krb5_db2_db_fini(kcontext);
1638 if (status)
1639 goto clean_n_exit;
1640
1641 status = krb5_db2_open(kcontext, conf_section, db_args, KRB5_KDB_OPEN_RW);
1642
1643 clean_n_exit:
1644 if (db_name)
1645 free(db_name);
1646 return status;
1647 }
1648
1649 krb5_error_code
krb5_db2_destroy(krb5_context kcontext,char * conf_section,char ** db_args)1650 krb5_db2_destroy(krb5_context kcontext, char *conf_section, char **db_args)
1651 {
1652 krb5_error_code status = 0;
1653 char **t_ptr = db_args;
1654 int db_name_set = 0, tempdb=0;
1655 char *db_name = NULL;
1656
1657 while (t_ptr && *t_ptr) {
1658 char *opt = NULL, *val = NULL;
1659
1660 krb5_db2_get_db_opt(*t_ptr, &opt, &val);
1661 if (opt && !strcmp(opt, "dbname")) {
1662 db_name = strdup(val);
1663 if (db_name == NULL)
1664 return ENOMEM;
1665 }
1666 else if (!opt && !strcmp(val, "temporary")) {
1667 tempdb = 1;
1668 }
1669 /* ignore hash argument. Might have been passed from create */
1670 else if (!opt || strcmp(opt, "hash")) {
1671 free(opt);
1672 free(val);
1673 return EINVAL;
1674 }
1675
1676 free(opt);
1677 free(val);
1678 t_ptr++;
1679 }
1680
1681 if (db_name) {
1682 status = krb5_db2_db_set_name(kcontext, db_name, tempdb);
1683 if (status) {
1684 goto clean_n_exit;
1685 }
1686 db_name_set = 1;
1687 }
1688 if (!db_name_set) {
1689 char *value = NULL;
1690 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME, /* under given conf section */
1691 NULL, &value);
1692
1693 if (value == NULL) {
1694 /* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */
1695 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME, /* under given realm */
1696 default_db_name, &value);
1697 if (status) {
1698 goto clean_n_exit;
1699 }
1700 }
1701
1702 db_name = strdup(value);
1703 if (db_name == NULL) {
1704 status = ENOMEM;
1705 goto clean_n_exit;
1706 }
1707 status = krb5_db2_db_set_name(kcontext, value, tempdb);
1708 profile_release_string(value);
1709 if (status) {
1710 goto clean_n_exit;
1711 }
1712
1713 }
1714
1715 status = krb5_db2_db_destroy(kcontext, db_name);
1716
1717 clean_n_exit:
1718 if (db_name)
1719 free(db_name);
1720 return status;
1721 }
1722
1723 krb5_error_code
krb5_db2_set_master_key_ext(krb5_context kcontext,char * pwd,krb5_keyblock * key)1724 krb5_db2_set_master_key_ext(krb5_context kcontext,
1725 char *pwd, krb5_keyblock * key)
1726 {
1727 return krb5_db2_db_set_mkey(kcontext, key);
1728 }
1729
1730 krb5_error_code
krb5_db2_db_set_option(krb5_context kcontext,int option,void * value)1731 krb5_db2_db_set_option(krb5_context kcontext, int option, void *value)
1732 {
1733 krb5_error_code status = 0;
1734 krb5_boolean oldval;
1735 krb5_db2_context *db_ctx;
1736 kdb5_dal_handle *dal_handle;
1737
1738 if (!k5db2_inited(kcontext))
1739 return KRB5_KDB_DBNOTINITED;
1740
1741 dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1742 db_ctx = (krb5_db2_context *) dal_handle->db_context;
1743
1744
1745 switch (option) {
1746 case KRB5_KDB_OPT_SET_DB_NAME:
1747 status = krb5_db2_db_set_name(kcontext, (char *) value, db_ctx->tempdb);
1748 break;
1749
1750 case KRB5_KDB_OPT_SET_LOCK_MODE:
1751 oldval = krb5_db2_db_set_lockmode(kcontext, *((krb5_boolean *) value));
1752 *((krb5_boolean *) value) = oldval;
1753 break;
1754
1755 default:
1756 status = -1; /* TBD */
1757 break;
1758 }
1759
1760 return status;
1761 }
1762
1763 void *
krb5_db2_alloc(krb5_context kcontext,void * ptr,size_t size)1764 krb5_db2_alloc(krb5_context kcontext, void *ptr, size_t size)
1765 {
1766 return realloc(ptr, size);
1767 }
1768
1769 void
krb5_db2_free(krb5_context kcontext,void * ptr)1770 krb5_db2_free(krb5_context kcontext, void *ptr)
1771 {
1772 free(ptr);
1773 }
1774
1775 /* policy functions */
1776 krb5_error_code
krb5_db2_create_policy(krb5_context kcontext,osa_policy_ent_t policy)1777 krb5_db2_create_policy(krb5_context kcontext, osa_policy_ent_t policy)
1778 {
1779 kdb5_dal_handle *dal_handle;
1780 krb5_db2_context *dbc;
1781
1782 dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1783 dbc = (krb5_db2_context *) dal_handle->db_context;
1784
1785 return osa_adb_create_policy(dbc->policy_db, policy);
1786 }
1787
1788 krb5_error_code
krb5_db2_get_policy(krb5_context kcontext,char * name,osa_policy_ent_t * policy,int * cnt)1789 krb5_db2_get_policy(krb5_context kcontext,
1790 char *name, osa_policy_ent_t * policy, int *cnt)
1791 {
1792 kdb5_dal_handle *dal_handle;
1793 krb5_db2_context *dbc;
1794
1795 dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1796 dbc = (krb5_db2_context *) dal_handle->db_context;
1797
1798 return osa_adb_get_policy(dbc->policy_db, name, policy, cnt);
1799 }
1800
1801 krb5_error_code
krb5_db2_put_policy(krb5_context kcontext,osa_policy_ent_t policy)1802 krb5_db2_put_policy(krb5_context kcontext, osa_policy_ent_t policy)
1803 {
1804 kdb5_dal_handle *dal_handle;
1805 krb5_db2_context *dbc;
1806
1807 dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1808 dbc = (krb5_db2_context *) dal_handle->db_context;
1809
1810 return osa_adb_put_policy(dbc->policy_db, policy);
1811 }
1812
1813 krb5_error_code
krb5_db2_iter_policy(krb5_context kcontext,char * match_entry,osa_adb_iter_policy_func func,void * data)1814 krb5_db2_iter_policy(krb5_context kcontext,
1815 char *match_entry,
1816 osa_adb_iter_policy_func func, void *data)
1817 {
1818 kdb5_dal_handle *dal_handle;
1819 krb5_db2_context *dbc;
1820
1821 dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1822 dbc = (krb5_db2_context *) dal_handle->db_context;
1823
1824 return osa_adb_iter_policy(dbc->policy_db, func, data);
1825 }
1826
1827 krb5_error_code
krb5_db2_delete_policy(krb5_context kcontext,char * policy)1828 krb5_db2_delete_policy(krb5_context kcontext, char *policy)
1829 {
1830 kdb5_dal_handle *dal_handle;
1831 krb5_db2_context *dbc;
1832
1833 dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1834 dbc = (krb5_db2_context *) dal_handle->db_context;
1835
1836 return osa_adb_destroy_policy(dbc->policy_db, policy);
1837 }
1838
1839 void
krb5_db2_free_policy(krb5_context kcontext,osa_policy_ent_t entry)1840 krb5_db2_free_policy(krb5_context kcontext, osa_policy_ent_t entry)
1841 {
1842 osa_free_policy_ent(entry);
1843 }
1844
1845
1846 /* */
1847
1848 krb5_error_code
krb5_db2_promote_db(krb5_context kcontext,char * conf_section,char ** db_args)1849 krb5_db2_promote_db(krb5_context kcontext, char *conf_section, char **db_args)
1850 {
1851 krb5_error_code status = 0;
1852 char *db_name = NULL;
1853 char *temp_db_name = NULL;
1854
1855 krb5_clear_error_message (kcontext);
1856
1857 {
1858 kdb5_dal_handle *dal_handle = kcontext->db_context;
1859 krb5_db2_context *db_ctx = dal_handle->db_context;
1860 db_name = strdup(db_ctx->db_name);
1861 if (db_name == NULL) {
1862 status = ENOMEM;
1863 goto clean_n_exit;
1864 }
1865 }
1866
1867 assert(kcontext->db_context != NULL);
1868 temp_db_name = gen_dbsuffix(db_name, "~");
1869 if (temp_db_name == NULL) {
1870 status = ENOMEM;
1871 goto clean_n_exit;
1872 }
1873
1874 status = krb5_db2_db_rename (kcontext, temp_db_name, db_name);
1875
1876 clean_n_exit:
1877 if (db_name)
1878 free(db_name);
1879 if (temp_db_name)
1880 free(temp_db_name);
1881 return status;
1882 }
1883
1884 /* Retrieved from pre-DAL code base. */
1885 /*
1886 * "Atomically" rename the database in a way that locks out read
1887 * access in the middle of the rename.
1888 *
1889 * Not perfect; if we crash in the middle of an update, we don't
1890 * necessarily know to complete the transaction the rename, but...
1891 *
1892 * Since the rename operation happens outside the init/fini bracket, we
1893 * have to go through the same stuff that we went through up in db_destroy.
1894 */
1895 krb5_error_code
krb5_db2_db_rename(context,from,to)1896 krb5_db2_db_rename(context, from, to)
1897 krb5_context context;
1898 char *from;
1899 char *to;
1900 {
1901 char *fromok;
1902 krb5_error_code retval;
1903 krb5_db2_context *s_context, *db_ctx;
1904 kdb5_dal_handle *dal_handle = context->db_context;
1905
1906 s_context = dal_handle->db_context;
1907 dal_handle->db_context = NULL;
1908 if ((retval = k5db2_init_context(context)))
1909 return retval;
1910 db_ctx = (krb5_db2_context *) dal_handle->db_context;
1911
1912 /*
1913 * Create the database if it does not already exist; the
1914 * files must exist because krb5_db2_db_lock, called below,
1915 * will fail otherwise.
1916 */
1917 {
1918 struct stat statbuf;
1919
1920 if (stat(to, &statbuf) == -1) {
1921 if (errno == ENOENT) {
1922 retval = krb5_db2_db_create(context, to,
1923 KRB5_KDB_CREATE_BTREE);
1924 if (retval)
1925 goto errout;
1926 }
1927 else {
1928 /*
1929 * XXX assuming we should bail if there is some other stat error
1930 */
1931 retval = errno;
1932 goto errout;
1933 }
1934 }
1935 }
1936 /*
1937 * Set the database to the target, so that other processes sharing
1938 * the target will stop their activity, and notice the new database.
1939 */
1940 retval = krb5_db2_db_set_name(context, to, 0);
1941 if (retval)
1942 goto errout;
1943
1944 retval = krb5_db2_db_init(context);
1945 if (retval)
1946 goto errout;
1947
1948 /* XXX WAF this needs to be redone (not lock safe)!!! */
1949 {
1950 /* Ugly brute force hack.
1951
1952 Should be going through nice friendly helper routines for
1953 this, but it's a mess of jumbled so-called interfaces right
1954 now. */
1955 char policy[2048], new_policy[2048];
1956 assert (strlen(db_ctx->db_name) < 2000);
1957 /*LINTED*/
1958 sprintf(policy, "%s.kadm5", db_ctx->db_name);
1959 /*LINTED*/
1960 sprintf(new_policy, "%s~.kadm5", db_ctx->db_name);
1961 if (0 != rename(new_policy, policy)) {
1962 retval = errno;
1963 goto errout;
1964 }
1965 strcat(new_policy, ".lock");
1966 (void) unlink(new_policy);
1967 }
1968
1969 retval = krb5_db2_db_get_age(context, NULL, &db_ctx->db_lf_time);
1970 if (retval)
1971 goto errout;
1972
1973 fromok = gen_dbsuffix(from, KDB2_LOCK_EXT);
1974 if (fromok == NULL) {
1975 retval = ENOMEM;
1976 goto errout;
1977 }
1978
1979 if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE)))
1980 goto errfromok;
1981
1982 if ((retval = krb5_db2_db_start_update(context)))
1983 goto errfromok;
1984
1985 if (rename(from, to)) {
1986 retval = errno;
1987 goto errfromok;
1988 }
1989 if (unlink(fromok)) {
1990 retval = errno;
1991 goto errfromok;
1992 }
1993 retval = krb5_db2_db_end_update(context);
1994 errfromok:
1995 free_dbsuffix(fromok);
1996 errout:
1997 if (dal_handle->db_context) {
1998 if (db_ctx->db_lf_file >= 0) {
1999 krb5_db2_db_unlock(context);
2000 close(db_ctx->db_lf_file);
2001 }
2002 k5db2_clear_context((krb5_db2_context *) dal_handle->db_context);
2003 free(dal_handle->db_context);
2004 }
2005
2006 dal_handle->db_context = s_context;
2007 (void) krb5_db2_db_unlock(context); /* unlock saved context db */
2008
2009 return retval;
2010 }
2011
2012 const char *
krb5_db2_errcode_2_string(krb5_context kcontext,long err_code)2013 krb5_db2_errcode_2_string(krb5_context kcontext, long err_code)
2014 {
2015 return krb5_get_error_message(kcontext, err_code);
2016 }
2017
2018 void
krb5_db2_release_errcode_string(krb5_context kcontext,const char * msg)2019 krb5_db2_release_errcode_string(krb5_context kcontext, const char *msg)
2020 {
2021 krb5_free_error_message(kcontext, msg);
2022 }
2023
2024
2025 /*
2026 * Solaris Kerberos:
2027 * Similar to the ldap plugin.
2028 */
2029 static void
krb5_db2_prepend_err_str(krb5_context ctx,const char * str,krb5_error_code err,krb5_error_code oerr)2030 krb5_db2_prepend_err_str(krb5_context ctx, const char *str, krb5_error_code err,
2031 krb5_error_code oerr) {
2032 const char *omsg;
2033 if (oerr == 0)
2034 oerr = err;
2035 omsg = krb5_get_error_message (ctx, err);
2036 krb5_set_error_message (ctx, err, "%s %s", str, omsg);
2037 krb5_free_error_message(ctx, omsg);
2038 }
2039
2040