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