krb5_error_code krb5_db2_put_principal(krb5_context context, krb5_db_entry *entry, char **db_args) { int dbret; DB *db; DBT key, contents; krb5_data contdata, keydata; krb5_error_code retval; krb5_db2_context *db_ctx; krb5_clear_error_message (context); if (db_args) { /* DB2 does not support db_args DB arguments for principal */ krb5_set_error_message(context, EINVAL, "Unsupported argument \"%s\" for db2", db_args[0]); return EINVAL; } if (!k5db2_inited(context)) return KRB5_KDB_DBNOTINITED; db_ctx = context->dal_handle->db_context; if ((retval = krb5_db2_lock(context, KRB5_LOCKMODE_EXCLUSIVE))) return retval; db = db_ctx->db; if ((retval = krb5_db2_start_update(context))) { (void) krb5_db2_unlock(context); return retval; } retval = krb5_encode_princ_entry(context, &contdata, entry); if (retval) goto cleanup; contents.data = contdata.data; contents.size = contdata.length; retval = krb5_encode_princ_dbkey(context, &keydata, entry->princ); if (retval) { krb5_free_data_contents(context, &contdata); goto cleanup; } key.data = keydata.data; key.size = keydata.length; dbret = (*db->put)(db, &key, &contents, 0); retval = dbret ? errno : 0; krb5_free_data_contents(context, &keydata); krb5_free_data_contents(context, &contdata); cleanup: (void) krb5_db2_end_update(context); (void) krb5_db2_unlock(context); /* unlock database */ return (retval); }
/* * Merge non-replicated attributes (that is, lockout-related * attributes and negative TL data types) from the old database * into the new one. * * Note: src_db is locked on success. */ static krb5_error_code krb5_db2_begin_nra_merge(krb5_context context, krb5_db2_context *src_db, krb5_db2_context *dst_db) { krb5_error_code retval; kdb5_dal_handle *dal_handle = context->dal_handle; struct nra_context nra; nra.kcontext = context; nra.db_context = dst_db; assert(dal_handle->db_context == dst_db); dal_handle->db_context = src_db; retval = krb5_db2_lock(context, KRB5_LOCKMODE_EXCLUSIVE); if (retval) { dal_handle->db_context = dst_db; return retval; } retval = krb5_db2_iterate_ext(context, krb5_db2_merge_nra_iterator, &nra, 0, 0); if (retval != 0) (void) krb5_db2_unlock(context); dal_handle->db_context = dst_db; return retval; }
krb5_error_code krb5_db2_get_principal(krb5_context context, krb5_const_principal searchfor, unsigned int flags, krb5_db_entry **entry) { krb5_db2_context *db_ctx; krb5_error_code retval; DB *db; DBT key, contents; krb5_data keydata, contdata; int trynum, dbret; *entry = NULL; if (!k5db2_inited(context)) return KRB5_KDB_DBNOTINITED; db_ctx = context->dal_handle->db_context; for (trynum = 0; trynum < KRB5_DB2_MAX_RETRY; trynum++) { if ((retval = krb5_db2_lock(context, KRB5_LOCKMODE_SHARED))) { if (db_ctx->db_nb_locks) return (retval); sleep(1); continue; } break; } if (trynum == KRB5_DB2_MAX_RETRY) return KRB5_KDB_DB_INUSE; /* XXX deal with wildcard lookups */ retval = krb5_encode_princ_dbkey(context, &keydata, searchfor); if (retval) goto cleanup; key.data = keydata.data; key.size = keydata.length; db = db_ctx->db; dbret = (*db->get)(db, &key, &contents, 0); retval = errno; krb5_free_data_contents(context, &keydata); switch (dbret) { case 1: retval = KRB5_KDB_NOENTRY; /* Fall through. */ case -1: default: goto cleanup; case 0: contdata.data = contents.data; contdata.length = contents.size; retval = krb5_decode_princ_entry(context, &contdata, entry); break; } cleanup: (void) krb5_db2_unlock(context); /* unlock read lock */ return retval; }
/* * Finish merge of non-replicated attributes by unlocking * src_db. */ static krb5_error_code krb5_db2_end_nra_merge(krb5_context context, krb5_db2_context *src_db, krb5_db2_context *dst_db) { krb5_error_code retval; kdb5_dal_handle *dal_handle = context->dal_handle; dal_handle->db_context = src_db; retval = krb5_db2_unlock(context); dal_handle->db_context = dst_db; return retval; }
krb5_error_code krb5_db2_get_principal(krb5_context context, krb5_const_principal searchfor, unsigned int flags, krb5_db_entry **entry) { krb5_db2_context *dbc; krb5_error_code retval; DB *db; DBT key, contents; krb5_data keydata, contdata; int dbret; *entry = NULL; if (!inited(context)) return KRB5_KDB_DBNOTINITED; dbc = context->dal_handle->db_context; retval = ctx_lock(context, dbc, KRB5_LOCKMODE_SHARED); if (retval) return retval; /* XXX deal with wildcard lookups */ retval = krb5_encode_princ_dbkey(context, &keydata, searchfor); if (retval) goto cleanup; key.data = keydata.data; key.size = keydata.length; db = dbc->db; dbret = (*db->get)(db, &key, &contents, 0); retval = errno; krb5_free_data_contents(context, &keydata); switch (dbret) { case 1: retval = KRB5_KDB_NOENTRY; /* Fall through. */ case -1: default: goto cleanup; case 0: contdata.data = contents.data; contdata.length = contents.size; retval = krb5_decode_princ_entry(context, &contdata, entry); break; } cleanup: (void) krb5_db2_unlock(context); /* unlock read lock */ return retval; }
krb5_error_code krb5_db2_create(krb5_context context, char *conf_section, char **db_args) { krb5_error_code status = 0; krb5_db2_context *dbc; krb5_clear_error_message(context); if (inited(context)) return 0; status = configure_context(context, conf_section, db_args); if (status != 0) return status; dbc = context->dal_handle->db_context; status = ctx_create_db(context, dbc); if (status != 0) return status; if (!dbc->tempdb) krb5_db2_unlock(context); return 0; }
krb5_error_code krb5_db2_delete_principal(krb5_context context, krb5_const_principal searchfor) { krb5_error_code retval; krb5_db_entry *entry; krb5_db2_context *db_ctx; DB *db; DBT key, contents; krb5_data keydata, contdata; int i, dbret; if (!k5db2_inited(context)) return KRB5_KDB_DBNOTINITED; db_ctx = context->dal_handle->db_context; if ((retval = krb5_db2_lock(context, KRB5_LOCKMODE_EXCLUSIVE))) return (retval); if ((retval = krb5_db2_start_update(context))) { (void) krb5_db2_unlock(context); /* unlock write lock */ return (retval); } if ((retval = krb5_encode_princ_dbkey(context, &keydata, searchfor))) goto cleanup; key.data = keydata.data; key.size = keydata.length; db = db_ctx->db; dbret = (*db->get) (db, &key, &contents, 0); retval = errno; switch (dbret) { case 1: retval = KRB5_KDB_NOENTRY; /* Fall through. */ case -1: default: goto cleankey; case 0: ; } contdata.data = contents.data; contdata.length = contents.size; retval = krb5_decode_princ_entry(context, &contdata, &entry); if (retval) goto cleankey; /* Clear encrypted key contents */ for (i = 0; i < entry->n_key_data; i++) { if (entry->key_data[i].key_data_length[0]) { memset(entry->key_data[i].key_data_contents[0], 0, (unsigned) entry->key_data[i].key_data_length[0]); } } retval = krb5_encode_princ_entry(context, &contdata, entry); krb5_dbe_free(context, entry); if (retval) goto cleankey; contents.data = contdata.data; contents.size = contdata.length; dbret = (*db->put) (db, &key, &contents, 0); retval = dbret ? errno : 0; krb5_free_data_contents(context, &contdata); if (retval) goto cleankey; dbret = (*db->del) (db, &key, 0); retval = dbret ? errno : 0; cleankey: krb5_free_data_contents(context, &keydata); cleanup: (void) krb5_db2_end_update(context); (void) krb5_db2_unlock(context); /* unlock write lock */ return retval; }
krb5_error_code krb5_db2_lock(krb5_context context, int in_mode) { krb5_db2_context *db_ctx; int krb5_lock_mode; DB *db; krb5_error_code retval; time_t mod_time; int mode, gotlock, tries; switch (in_mode) { case KRB5_DB_LOCKMODE_PERMANENT: mode = KRB5_DB_LOCKMODE_EXCLUSIVE; break; case KRB5_DB_LOCKMODE_EXCLUSIVE: mode = KRB5_LOCKMODE_EXCLUSIVE; break; case KRB5_DB_LOCKMODE_SHARED: mode = KRB5_LOCKMODE_SHARED; break; default: return EINVAL; } if (!k5db2_inited(context)) return KRB5_KDB_DBNOTINITED; db_ctx = context->dal_handle->db_context; if (db_ctx->db_locks_held && (db_ctx->db_lock_mode >= mode)) { /* No need to upgrade lock, just return */ db_ctx->db_locks_held++; goto policy_lock; } if ((mode != KRB5_LOCKMODE_SHARED) && (mode != KRB5_LOCKMODE_EXCLUSIVE)) return KRB5_KDB_BADLOCKMODE; krb5_lock_mode = mode | KRB5_LOCKMODE_DONTBLOCK; for (gotlock = tries = 0; tries < MAX_LOCK_TRIES; tries++) { retval = krb5_lock_file(context, db_ctx->db_lf_file, krb5_lock_mode); if (retval == 0) { gotlock++; break; } else if (retval == EBADF && mode == KRB5_DB_LOCKMODE_EXCLUSIVE) /* tried to exclusive-lock something we don't have */ /* write access to */ return KRB5_KDB_CANTLOCK_DB; sleep(1); } if (retval == EACCES) return KRB5_KDB_CANTLOCK_DB; else if (retval == EAGAIN || retval == EWOULDBLOCK) return OSA_ADB_CANTLOCK_DB; else if (retval != 0) return retval; if ((retval = krb5_db2_get_age(context, NULL, &mod_time))) goto lock_error; db = k5db2_dbopen(db_ctx, db_ctx->db_name, mode == KRB5_LOCKMODE_SHARED ? O_RDONLY : O_RDWR, 0600, db_ctx->tempdb); if (db) { db_ctx->db_lf_time = mod_time; db_ctx->db = db; } else { retval = errno; db_ctx->db = NULL; goto lock_error; } db_ctx->db_lock_mode = mode; db_ctx->db_locks_held++; policy_lock: if ((retval = osa_adb_get_lock(db_ctx->policy_db, in_mode))) { krb5_db2_unlock(context); } return retval; lock_error:; db_ctx->db_lock_mode = 0; db_ctx->db_locks_held = 0; krb5_db2_unlock(context); return retval; }
/* * "Atomically" rename the database in a way that locks out read * access in the middle of the rename. * * Not perfect; if we crash in the middle of an update, we don't * necessarily know to complete the transaction the rename, but... * * Since the rename operation happens outside the init/fini bracket, we * have to go through the same stuff that we went through up in db_destroy. */ krb5_error_code krb5_db2_rename(krb5_context context, char *from, char *to, int merge_nra) { char *fromok; krb5_error_code retval; krb5_db2_context *s_context, *db_ctx; kdb5_dal_handle *dal_handle = context->dal_handle; s_context = dal_handle->db_context; dal_handle->db_context = NULL; if ((retval = k5db2_init_context(context))) return retval; db_ctx = (krb5_db2_context *) dal_handle->db_context; /* * Create the database if it does not already exist; the * files must exist because krb5_db2_lock, called below, * will fail otherwise. */ retval = create_db(context, to); if (retval != 0 && retval != EEXIST) goto errout; /* * Set the database to the target, so that other processes sharing * the target will stop their activity, and notice the new database. */ db_ctx->db_name = strdup(to); if (db_ctx->db_name == NULL) { retval = ENOMEM; goto errout; } retval = check_openable(context); if (retval) goto errout; retval = krb5_db2_init(context); if (retval) goto errout; db_ctx->db_lf_name = gen_dbsuffix(db_ctx->db_name, KDB2_LOCK_EXT); if (db_ctx->db_lf_name == NULL) { retval = ENOMEM; goto errout; } db_ctx->db_lf_file = open(db_ctx->db_lf_name, O_RDWR|O_CREAT, 0600); if (db_ctx->db_lf_file < 0) { retval = errno; goto errout; } set_cloexec_fd(db_ctx->db_lf_file); db_ctx->db_inited = 1; retval = krb5_db2_get_age(context, NULL, &db_ctx->db_lf_time); if (retval) goto errout; fromok = gen_dbsuffix(from, KDB2_LOCK_EXT); if (fromok == NULL) { retval = ENOMEM; goto errout; } if ((retval = krb5_db2_lock(context, KRB5_LOCKMODE_EXCLUSIVE))) goto errfromok; if ((retval = krb5_db2_start_update(context))) goto errfromok; if (merge_nra) { if ((retval = krb5_db2_begin_nra_merge(context, s_context, db_ctx))) goto errfromok; } if (rename(from, to)) { retval = errno; goto errfromok; } if (unlink(fromok)) { retval = errno; goto errfromok; } if (merge_nra) { krb5_db2_end_nra_merge(context, s_context, db_ctx); } retval = krb5_db2_end_update(context); if (retval) goto errfromok; { /* XXX moved so that NRA merge works */ /* Ugly brute force hack. Should be going through nice friendly helper routines for this, but it's a mess of jumbled so-called interfaces right now. */ char policy[2048], new_policy[2048]; assert (strlen(db_ctx->db_name) < 2000); snprintf(policy, sizeof(policy), "%s.kadm5", db_ctx->db_name); snprintf(new_policy, sizeof(new_policy), "%s~.kadm5", db_ctx->db_name); if (0 != rename(new_policy, policy)) { retval = errno; goto errfromok; } strlcat(new_policy, ".lock",sizeof(new_policy)); (void) unlink(new_policy); } errfromok: free_dbsuffix(fromok); errout: if (dal_handle->db_context) { if (db_ctx->db_lf_file >= 0) { krb5_db2_unlock(context); close(db_ctx->db_lf_file); } k5db2_clear_context((krb5_db2_context *) dal_handle->db_context); free(dal_handle->db_context); } dal_handle->db_context = s_context; (void) krb5_db2_unlock(context); /* unlock saved context db */ return retval; }
krb5_error_code krb5_db2_iterate_ext(krb5_context context, krb5_error_code(*func) (krb5_pointer, krb5_db_entry *), krb5_pointer func_arg, int backwards, int recursive) { krb5_db2_context *db_ctx; DB *db; DBT key, contents; krb5_data contdata; krb5_db_entry *entry; krb5_error_code retval; int dbret; void *cookie; cookie = NULL; if (!k5db2_inited(context)) return KRB5_KDB_DBNOTINITED; db_ctx = context->dal_handle->db_context; retval = krb5_db2_lock(context, KRB5_LOCKMODE_SHARED); if (retval) return retval; db = db_ctx->db; if (recursive && db->type != DB_BTREE) { (void) krb5_db2_unlock(context); return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */ } if (!recursive) { dbret = (*db->seq) (db, &key, &contents, backwards ? R_LAST : R_FIRST); } else { #ifdef HAVE_BT_RSEQ dbret = bt_rseq(db, &key, &contents, &cookie, backwards ? R_LAST : R_FIRST); #else (void) krb5_db2_unlock(context); return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */ #endif } while (dbret == 0) { krb5_error_code retval2; contdata.data = contents.data; contdata.length = contents.size; retval = krb5_decode_princ_entry(context, &contdata, &entry); if (retval) break; retval = k5_mutex_unlock(krb5_db2_mutex); if (retval) break; retval = (*func)(func_arg, entry); krb5_dbe_free(context, entry); retval2 = k5_mutex_lock(krb5_db2_mutex); /* Note: If re-locking fails, the wrapper in db2_exp.c will still try to unlock it again. That would be a bug. Fix when integrating the locking better. */ if (retval) break; if (retval2) { retval = retval2; break; } if (!recursive) { dbret = (*db->seq) (db, &key, &contents, backwards ? R_PREV : R_NEXT); } else { #ifdef HAVE_BT_RSEQ dbret = bt_rseq(db, &key, &contents, &cookie, backwards ? R_PREV : R_NEXT); #else (void) krb5_db2_unlock(context); return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */ #endif } } switch (dbret) { case 1: case 0: break; case -1: default: retval = errno; } (void) krb5_db2_unlock(context); return retval; }
krb5_error_code krb5_db2_promote_db(krb5_context context, char *conf_section, char **db_args) { krb5_error_code retval; krb5_boolean merge_nra = FALSE, real_locked = FALSE; krb5_db2_context *dbc_temp, *dbc_real = NULL; char **db_argp; /* context must be initialized with an exclusively locked temp DB. */ if (!inited(context)) return KRB5_KDB_DBNOTINITED; dbc_temp = context->dal_handle->db_context; if (dbc_temp->db_lock_mode != KRB5_LOCKMODE_EXCLUSIVE) return KRB5_KDB_NOTLOCKED; if (!dbc_temp->tempdb) return EINVAL; /* Check db_args for whether we should merge non-replicated attributes. */ for (db_argp = db_args; *db_argp; db_argp++) { if (!strcmp(*db_argp, "merge_nra")) { merge_nra = TRUE; break; } } /* Make a db2 context for the real DB. */ dbc_real = k5alloc(sizeof(*dbc_real), &retval); if (dbc_real == NULL) return retval; ctx_clear(dbc_real); /* Try creating the real DB. */ dbc_real->db_name = strdup(dbc_temp->db_name); if (dbc_real->db_name == NULL) goto cleanup; dbc_real->tempdb = FALSE; retval = ctx_create_db(context, dbc_real); if (retval == EEXIST) { /* The real database already exists, so open and lock it. */ dbc_real->db_name = strdup(dbc_temp->db_name); if (dbc_real->db_name == NULL) goto cleanup; dbc_real->tempdb = FALSE; retval = ctx_init(dbc_real); if (retval) goto cleanup; retval = ctx_lock(context, dbc_real, KRB5_DB_LOCKMODE_EXCLUSIVE); if (retval) goto cleanup; } else if (retval) goto cleanup; real_locked = TRUE; if (merge_nra) { retval = ctx_merge_nra(context, dbc_temp, dbc_real); if (retval) goto cleanup; } /* Perform filesystem manipulations for the promotion. */ retval = ctx_promote(context, dbc_temp, dbc_real); if (retval) goto cleanup; /* Unlock and finalize context since the temp DB is gone. */ (void) krb5_db2_unlock(context); krb5_db2_fini(context); cleanup: if (real_locked) (void) ctx_unlock(context, dbc_real); if (dbc_real) ctx_fini(dbc_real); return retval; }