int iprop_replay(struct replay_options *opt, int argc, char **argv) { kadm5_server_context *server_context; krb5_error_code ret; server_context = get_kadmin_context(opt->config_file_string, opt->realm_string); ret = server_context->db->hdb_open(context, server_context->db, O_RDWR | O_CREAT, 0600); if (ret) krb5_err (context, 1, ret, "db->open"); ret = kadm5_log_init (server_context); if (ret) krb5_err (context, 1, ret, "kadm5_log_init"); ret = kadm5_log_foreach (server_context, apply_entry, opt); if(ret) krb5_warn(context, ret, "kadm5_log_foreach"); ret = kadm5_log_end (server_context); if (ret) krb5_warn(context, ret, "kadm5_log_end"); ret = server_context->db->hdb_close (context, server_context->db); if (ret) krb5_err (context, 1, ret, "db->close"); return 0; }
kadm5_ret_t kadm5_log_truncate (kadm5_server_context *server_context) { kadm5_ret_t ret; u_int32_t vno; ret = kadm5_log_init (server_context); if (ret) return ret; ret = kadm5_log_get_version (server_context, &vno); if (ret) return ret; ret = kadm5_log_reinit (server_context); if (ret) return ret; ret = kadm5_log_set_version (server_context, vno + 1); if (ret) return ret; ret = kadm5_log_nop (server_context); if (ret) return ret; ret = kadm5_log_end (server_context); if (ret) return ret; return 0; }
int last_version(struct last_version_options *opt, int argc, char **argv) { kadm5_server_context *server_context; krb5_error_code ret; uint32_t version; server_context = get_kadmin_context(opt->config_file_string, opt->realm_string); ret = kadm5_log_init (server_context); if (ret) krb5_err (context, 1, ret, "kadm5_log_init"); ret = kadm5_log_get_version (server_context, &version); if (ret) krb5_err (context, 1, ret, "kadm5_log_get_version"); ret = kadm5_log_end (server_context); if (ret) krb5_warn(context, ret, "kadm5_log_end"); printf("version: %lu\n", (unsigned long)version); return 0; }
kadm5_ret_t kadm5_s_create_principal_with_key(void *server_handle, kadm5_principal_ent_t princ, uint32_t mask) { kadm5_ret_t ret; hdb_entry_ex ent; kadm5_server_context *context = server_handle; if ((mask & KADM5_KVNO) == 0) { /* create_principal() through _kadm5_setup_entry(), will need this */ princ->kvno = 1; mask |= KADM5_KVNO; } ret = create_principal(context, princ, mask, &ent, KADM5_PRINCIPAL | KADM5_KEY_DATA, KADM5_LAST_PWD_CHANGE | KADM5_MOD_TIME | KADM5_MOD_NAME | KADM5_MKVNO | KADM5_AUX_ATTRIBUTES | KADM5_POLICY_CLR | KADM5_LAST_SUCCESS | KADM5_LAST_FAILED | KADM5_FAIL_AUTH_COUNT); if (ret) return ret; if (!context->keep_open) { ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0); if (ret) { hdb_free_entry(context->context, &ent); return ret; } } ret = kadm5_log_init(context); if (ret) goto out; ret = hdb_seal_keys(context->context, context->db, &ent.entry); if (ret) goto out2; /* This logs the change for iprop and writes to the HDB */ ret = kadm5_log_create(context, &ent.entry); out2: (void) kadm5_log_end(context); out: if (!context->keep_open) { kadm5_ret_t ret2; ret2 = context->db->hdb_close(context->context, context->db); if (ret == 0 && ret2 != 0) ret = ret2; } hdb_free_entry(context->context, &ent); return _kadm5_error_code(ret); }
static kadm5_ret_t kadm5_log_preamble (kadm5_server_context *context, krb5_storage *sp, enum kadm_ops op) { kadm5_log_context *log_context = &context->log_context; kadm5_ret_t kadm_ret; kadm_ret = kadm5_log_init (context); if (kadm_ret) return kadm_ret; krb5_store_int32 (sp, ++log_context->version); krb5_store_int32 (sp, time(NULL)); krb5_store_int32 (sp, op); return 0; }
int iprop_dump(struct dump_options *opt, int argc, char **argv) { kadm5_server_context *server_context; krb5_error_code ret; server_context = get_kadmin_context(opt->config_file_string, opt->realm_string); ret = kadm5_log_init (server_context); if (ret) krb5_err (context, 1, ret, "kadm5_log_init"); ret = kadm5_log_foreach (server_context, print_entry, NULL); if(ret) krb5_warn(context, ret, "kadm5_log_foreach"); ret = kadm5_log_end (server_context); if (ret) krb5_warn(context, ret, "kadm5_log_end"); return 0; }
int main(int argc, char **argv) { krb5_error_code ret; krb5_context context; krb5_auth_context auth_context; void *kadm_handle; kadm5_server_context *server_context; kadm5_config_params conf; int master_fd; krb5_ccache ccache; krb5_principal server; char **files; int optidx = 0; time_t reconnect_min; time_t backoff; time_t reconnect_max; time_t reconnect; time_t before = 0; const char *master; setprogname(argv[0]); if(getarg(args, num_args, argc, argv, &optidx)) usage(1); if(help_flag) usage(0); if(version_flag) { print_version(NULL); exit(0); } ret = krb5_init_context(&context); if (ret) errx (1, "krb5_init_context failed: %d", ret); setup_signal(); if (config_file == NULL) { asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context)); if (config_file == NULL) errx(1, "out of memory"); } ret = krb5_prepend_config_files_default(config_file, &files); if (ret) krb5_err(context, 1, ret, "getting configuration files"); ret = krb5_set_config_files(context, files); krb5_free_config_files(files); if (ret) krb5_err(context, 1, ret, "reading configuration files"); argc -= optidx; argv += optidx; if (argc != 1) usage(1); master = argv[0]; #ifdef SUPPORT_DETACH if (detach_from_console) daemon(0, 0); #endif pidfile (NULL); krb5_openlog (context, "ipropd-slave", &log_facility); krb5_set_warn_dest(context, log_facility); ret = krb5_kt_register(context, &hdb_kt_ops); if(ret) krb5_err(context, 1, ret, "krb5_kt_register"); time_before_lost = parse_time (server_time_lost, "s"); if (time_before_lost < 0) krb5_errx (context, 1, "couldn't parse time: %s", server_time_lost); memset(&conf, 0, sizeof(conf)); if(realm) { conf.mask |= KADM5_CONFIG_REALM; conf.realm = realm; } ret = kadm5_init_with_password_ctx (context, KADM5_ADMIN_SERVICE, NULL, KADM5_ADMIN_SERVICE, &conf, 0, 0, &kadm_handle); if (ret) krb5_err (context, 1, ret, "kadm5_init_with_password_ctx"); server_context = (kadm5_server_context *)kadm_handle; ret = kadm5_log_init (server_context); if (ret) krb5_err (context, 1, ret, "kadm5_log_init"); get_creds(context, keytab_str, &ccache, master); ret = krb5_sname_to_principal (context, master, IPROP_NAME, KRB5_NT_SRV_HST, &server); if (ret) krb5_err (context, 1, ret, "krb5_sname_to_principal"); auth_context = NULL; master_fd = -1; krb5_appdefault_time(context, config_name, NULL, "reconnect-min", 10, &reconnect_min); krb5_appdefault_time(context, config_name, NULL, "reconnect-max", 300, &reconnect_max); krb5_appdefault_time(context, config_name, NULL, "reconnect-backoff", 10, &backoff); reconnect = reconnect_min; while (!exit_flag) { time_t now, elapsed; int connected = FALSE; now = time(NULL); elapsed = now - before; if (elapsed < reconnect) { time_t left = reconnect - elapsed; krb5_warnx(context, "sleeping %d seconds before " "retrying to connect", (int)left); sleep(left); } before = now; master_fd = connect_to_master (context, master, port_str); if (master_fd < 0) goto retry; reconnect = reconnect_min; if (auth_context) { krb5_auth_con_free(context, auth_context); auth_context = NULL; krb5_cc_destroy(context, ccache); get_creds(context, keytab_str, &ccache, master); } ret = krb5_sendauth (context, &auth_context, &master_fd, IPROP_VERSION, NULL, server, AP_OPTS_MUTUAL_REQUIRED, NULL, NULL, ccache, NULL, NULL, NULL); if (ret) { krb5_warn (context, ret, "krb5_sendauth"); goto retry; } krb5_warnx(context, "ipropd-slave started at version: %ld", (long)server_context->log_context.version); ret = ihave (context, auth_context, master_fd, server_context->log_context.version); if (ret) goto retry; connected = TRUE; while (connected && !exit_flag) { krb5_data out; krb5_storage *sp; int32_t tmp; fd_set readset; struct timeval to; #ifndef NO_LIMIT_FD_SETSIZE if (master_fd >= FD_SETSIZE) krb5_errx (context, 1, "fd too large"); #endif FD_ZERO(&readset); FD_SET(master_fd, &readset); to.tv_sec = time_before_lost; to.tv_usec = 0; ret = select (master_fd + 1, &readset, NULL, NULL, &to); if (ret < 0) { if (errno == EINTR) continue; else krb5_err (context, 1, errno, "select"); } if (ret == 0) krb5_errx (context, 1, "server didn't send a message " "in %d seconds", time_before_lost); ret = krb5_read_priv_message(context, auth_context, &master_fd, &out); if (ret) { krb5_warn (context, ret, "krb5_read_priv_message"); connected = FALSE; continue; } sp = krb5_storage_from_mem (out.data, out.length); krb5_ret_int32 (sp, &tmp); switch (tmp) { case FOR_YOU : receive (context, sp, server_context); ret = ihave (context, auth_context, master_fd, server_context->log_context.version); if (ret) connected = FALSE; break; case TELL_YOU_EVERYTHING : ret = receive_everything (context, master_fd, server_context, auth_context); if (ret) connected = FALSE; break; case ARE_YOU_THERE : send_im_here (context, master_fd, auth_context); break; case NOW_YOU_HAVE : case I_HAVE : case ONE_PRINC : case I_AM_HERE : default : krb5_warnx (context, "Ignoring command %d", tmp); break; } krb5_storage_free (sp); krb5_data_free (&out); } retry: if (connected == FALSE) krb5_warnx (context, "disconnected for server"); if (exit_flag) krb5_warnx (context, "got an exit signal"); if (master_fd >= 0) close(master_fd); reconnect += backoff; if (reconnect > reconnect_max) reconnect = reconnect_max; } if (0); #ifndef NO_SIGXCPU else if(exit_flag == SIGXCPU) krb5_warnx(context, "%s CPU time limit exceeded", getprogname()); #endif else if(exit_flag == SIGINT || exit_flag == SIGTERM) krb5_warnx(context, "%s terminated", getprogname()); else krb5_warnx(context, "%s unexpected exit reason: %ld", getprogname(), (long)exit_flag); return 0; }
static kadm5_ret_t modify_principal(void *server_handle, kadm5_principal_ent_t princ, uint32_t mask, uint32_t forbidden_mask) { kadm5_server_context *context = server_handle; hdb_entry_ex ent; kadm5_ret_t ret; memset(&ent, 0, sizeof(ent)); if((mask & forbidden_mask)) return KADM5_BAD_MASK; if((mask & KADM5_POLICY) && strcmp(princ->policy, "default")) return KADM5_UNK_POLICY; if (!context->keep_open) { ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0); if(ret) return ret; } ret = kadm5_log_init(context); if (ret) goto out; ret = context->db->hdb_fetch_kvno(context->context, context->db, princ->principal, HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent); if(ret) goto out; ret = _kadm5_setup_entry(context, &ent, mask, princ, mask, NULL, 0); if(ret) goto out2; ret = _kadm5_set_modifier(context, &ent.entry); if(ret) goto out2; /* * If any keys are bogus, disallow the modify. If the keys were * bogus as stored in the HDB we could allow those through, but * distinguishing that case from a pre-1.6 client using add_enctype * without the get-keys privilege requires more work (mainly: checking that * the bogus keys in princ->key_data[] have corresponding bogus keys in ent * before calling _kadm5_setup_entry()). */ if ((mask & KADM5_KEY_DATA) && kadm5_some_keys_are_bogus(princ->n_key_data, princ->key_data)) { ret = KADM5_AUTH_GET_KEYS; /* Not quite appropriate, but it'll do */ goto out2; } ret = hdb_seal_keys(context->context, context->db, &ent.entry); if (ret) goto out2; if ((mask & KADM5_POLICY)) { HDB_extension ext; memset(&ext, 0, sizeof(ext)); /* XXX should be TRUE, but we don't yet support policies */ ext.mandatory = FALSE; ext.data.element = choice_HDB_extension_data_policy; ext.data.u.policy = strdup(princ->policy); if (ext.data.u.policy == NULL) { ret = ENOMEM; goto out2; } /* This calls free_HDB_extension(), freeing ext.data.u.policy */ ret = hdb_replace_extension(context->context, &ent.entry, &ext); free(ext.data.u.policy); if (ret) goto out2; } /* This logs the change for iprop and writes to the HDB */ ret = kadm5_log_modify(context, &ent.entry, mask | KADM5_MOD_NAME | KADM5_MOD_TIME); out2: hdb_free_entry(context->context, &ent); out: (void) kadm5_log_end(context); if (!context->keep_open) { kadm5_ret_t ret2; ret2 = context->db->hdb_close(context->context, context->db); if (ret == 0 && ret2 != 0) ret = ret2; } return _kadm5_error_code(ret); }
/** * Server-side function to set new keys for a principal. */ kadm5_ret_t kadm5_s_setkey_principal_3(void *server_handle, krb5_principal princ, krb5_boolean keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, krb5_keyblock *keyblocks, int n_keys) { kadm5_server_context *context = server_handle; hdb_entry_ex ent; kadm5_ret_t ret = 0; memset(&ent, 0, sizeof(ent)); if (!context->keep_open) ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0); if (ret) return ret; ret = kadm5_log_init(context); if (ret) { if (!context->keep_open) context->db->hdb_close(context->context, context->db); return ret; } ret = context->db->hdb_fetch_kvno(context->context, context->db, princ, HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent); if (ret) { (void) kadm5_log_end(context); if (!context->keep_open) context->db->hdb_close(context->context, context->db); return ret; } if (keepold) { ret = hdb_add_current_keys_to_history(context->context, &ent.entry); } else ret = hdb_clear_extension(context->context, &ent.entry, choice_HDB_extension_data_hist_keys); /* * Though in practice all real calls to this function will pass an empty * ks_tuple, and cannot in any case employ any salts that require * additional data, we go the extra mile to set any requested salt type * along with a zero length salt value. While we're at it we check that * each ks_tuple's enctype matches the corresponding key enctype. */ if (ret == 0) { int i; free_Keys(&ent.entry.keys); for (i = 0; i < n_keys; ++i) { Key k; Salt s; k.mkvno = 0; k.key = keyblocks[i]; if (n_ks_tuple == 0) k.salt = 0; else { if (ks_tuple[i].ks_enctype != keyblocks[i].keytype) { ret = KADM5_SETKEY3_ETYPE_MISMATCH; break; } s.type = ks_tuple[i].ks_salttype; s.salt.data = 0; s.opaque = 0; k.salt = &s; } if ((ret = add_Keys(&ent.entry.keys, &k)) != 0) break; } } if (ret == 0) { ent.entry.kvno++; ent.entry.flags.require_pwchange = 0; hdb_entry_set_pw_change_time(context->context, &ent.entry, 0); hdb_entry_clear_password(context->context, &ent.entry); if ((ret = hdb_seal_keys(context->context, context->db, &ent.entry)) == 0 && (ret = _kadm5_set_modifier(context, &ent.entry)) == 0 && (ret = _kadm5_bump_pw_expire(context, &ent.entry)) == 0) ret = kadm5_log_modify(context, &ent.entry, KADM5_ATTRIBUTES | KADM5_PRINCIPAL | KADM5_MOD_NAME | KADM5_MOD_TIME | KADM5_KEY_DATA | KADM5_KVNO | KADM5_PW_EXPIRATION | KADM5_TL_DATA); } hdb_free_entry(context->context, &ent); (void) kadm5_log_end(context); if (!context->keep_open) context->db->hdb_close(context->context, context->db); return _kadm5_error_code(ret); }
static kadm5_ret_t change(void *server_handle, krb5_principal princ, int keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, const char *password, int cond) { kadm5_server_context *context = server_handle; hdb_entry_ex ent; kadm5_ret_t ret; Key *keys; size_t num_keys; int existsp = 0; memset(&ent, 0, sizeof(ent)); if (!context->keep_open) { ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0); if(ret) return ret; } ret = kadm5_log_init(context); if (ret) goto out; ret = context->db->hdb_fetch_kvno(context->context, context->db, princ, HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent); if (ret) goto out2; if (keepold || cond) { /* * We save these for now so we can handle password history checking; * we handle keepold further below. */ ret = hdb_add_current_keys_to_history(context->context, &ent.entry); if (ret) goto out3; } if (context->db->hdb_capability_flags & HDB_CAP_F_HANDLE_PASSWORDS) { ret = context->db->hdb_password(context->context, context->db, &ent, password, cond); if (ret) goto out3; } else { num_keys = ent.entry.keys.len; keys = ent.entry.keys.val; ent.entry.keys.len = 0; ent.entry.keys.val = NULL; ret = _kadm5_set_keys(context, &ent.entry, n_ks_tuple, ks_tuple, password); if(ret) { _kadm5_free_keys(context->context, num_keys, keys); goto out3; } _kadm5_free_keys(context->context, num_keys, keys); if (cond) { HDB_extension *ext; ext = hdb_find_extension(&ent.entry, choice_HDB_extension_data_hist_keys); if (ext != NULL) existsp = _kadm5_exists_keys_hist(ent.entry.keys.val, ent.entry.keys.len, &ext->data.u.hist_keys); } if (existsp) { ret = KADM5_PASS_REUSE; krb5_set_error_message(context->context, ret, "Password reuse forbidden"); goto out3; } } ent.entry.kvno++; ent.entry.flags.require_pwchange = 0; if (!keepold) { HDB_extension ext; memset(&ext, 0, sizeof (ext)); ext.mandatory = FALSE; ext.data.element = choice_HDB_extension_data_hist_keys; ret = hdb_replace_extension(context->context, &ent.entry, &ext); if (ret) goto out3; } ret = hdb_seal_keys(context->context, context->db, &ent.entry); if (ret) goto out3; ret = _kadm5_set_modifier(context, &ent.entry); if(ret) goto out3; ret = _kadm5_bump_pw_expire(context, &ent.entry); if (ret) goto out3; /* This logs the change for iprop and writes to the HDB */ ret = kadm5_log_modify(context, &ent.entry, KADM5_ATTRIBUTES | KADM5_PRINCIPAL | KADM5_MOD_NAME | KADM5_MOD_TIME | KADM5_KEY_DATA | KADM5_KVNO | KADM5_PW_EXPIRATION | KADM5_TL_DATA); out3: hdb_free_entry(context->context, &ent); out2: (void) kadm5_log_end(context); out: if (!context->keep_open) { kadm5_ret_t ret2; ret2 = context->db->hdb_close(context->context, context->db); if (ret == 0 && ret2 != 0) ret = ret2; } return _kadm5_error_code(ret); }
kadm5_ret_t kadm5_s_chpass_principal_with_key(void *server_handle, krb5_principal princ, int keepold, int n_key_data, krb5_key_data *key_data) { kadm5_server_context *context = server_handle; hdb_entry_ex ent; kadm5_ret_t ret; memset(&ent, 0, sizeof(ent)); if (!context->keep_open) { ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0); if(ret) return ret; } ret = kadm5_log_init(context); if (ret) goto out; ret = context->db->hdb_fetch_kvno(context->context, context->db, princ, 0, HDB_F_GET_ANY|HDB_F_ADMIN_DATA, &ent); if (ret == HDB_ERR_NOENTRY) goto out2; if (keepold) { ret = hdb_add_current_keys_to_history(context->context, &ent.entry); if (ret) goto out3; } ret = _kadm5_set_keys2(context, &ent.entry, n_key_data, key_data); if (ret) goto out3; ent.entry.kvno++; ret = _kadm5_set_modifier(context, &ent.entry); if (ret) goto out3; ret = _kadm5_bump_pw_expire(context, &ent.entry); if (ret) goto out3; if (keepold) { ret = hdb_seal_keys(context->context, context->db, &ent.entry); if (ret) goto out3; } else { HDB_extension ext; memset(&ext, 0, sizeof (ext)); ext.mandatory = FALSE; ext.data.element = choice_HDB_extension_data_hist_keys; ext.data.u.hist_keys.len = 0; ext.data.u.hist_keys.val = NULL; hdb_replace_extension(context->context, &ent.entry, &ext); } /* This logs the change for iprop and writes to the HDB */ ret = kadm5_log_modify(context, &ent.entry, KADM5_PRINCIPAL | KADM5_MOD_NAME | KADM5_MOD_TIME | KADM5_KEY_DATA | KADM5_KVNO | KADM5_PW_EXPIRATION | KADM5_TL_DATA); out3: hdb_free_entry(context->context, &ent); out2: (void) kadm5_log_end(context); out: if (!context->keep_open) { kadm5_ret_t ret2; ret2 = context->db->hdb_close(context->context, context->db); if (ret == 0 && ret2 != 0) ret = ret2; } return _kadm5_error_code(ret); }
int main(int argc, char **argv) { krb5_error_code ret, ret2; krb5_context context; krb5_auth_context auth_context; void *kadm_handle; kadm5_server_context *server_context; kadm5_config_params conf; int master_fd; krb5_ccache ccache; krb5_principal server; char **files; int optidx = 0; time_t reconnect_min; time_t backoff; time_t reconnect_max; time_t reconnect; time_t before = 0; int restarter_fd = -1; const char *master; setprogname(argv[0]); if (getarg(args, num_args, argc, argv, &optidx)) usage(1); if (help_flag) usage(0); if (version_flag) { print_version(NULL); exit(0); } if (detach_from_console && daemon_child == -1) roken_detach_prep(argc, argv, "--daemon-child"); rk_pidfile(NULL); ret = krb5_init_context(&context); if (ret) errx (1, "krb5_init_context failed: %d", ret); setup_signal(); if (config_file == NULL) { if (asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context)) == -1 || config_file == NULL) errx(1, "out of memory"); } ret = krb5_prepend_config_files_default(config_file, &files); if (ret) krb5_err(context, 1, ret, "getting configuration files"); ret = krb5_set_config_files(context, files); krb5_free_config_files(files); if (ret) krb5_err(context, 1, ret, "reading configuration files"); argc -= optidx; argv += optidx; if (argc != 1) usage(1); master = argv[0]; if (status_file == NULL) { if (asprintf(&status_file, "%s/ipropd-slave-status", hdb_db_dir(context)) < 0 || status_file == NULL) krb5_errx(context, 1, "can't allocate status file buffer"); } krb5_openlog(context, "ipropd-slave", &log_facility); krb5_set_warn_dest(context, log_facility); slave_status(context, status_file, "bootstrapping"); ret = krb5_kt_register(context, &hdb_get_kt_ops); if(ret) krb5_err(context, 1, ret, "krb5_kt_register"); time_before_lost = parse_time (server_time_lost, "s"); if (time_before_lost < 0) krb5_errx (context, 1, "couldn't parse time: %s", server_time_lost); slave_status(context, status_file, "getting credentials from keytab/database"); memset(&conf, 0, sizeof(conf)); if(realm) { conf.mask |= KADM5_CONFIG_REALM; conf.realm = realm; } ret = kadm5_init_with_password_ctx (context, KADM5_ADMIN_SERVICE, NULL, KADM5_ADMIN_SERVICE, &conf, 0, 0, &kadm_handle); if (ret) krb5_err (context, 1, ret, "kadm5_init_with_password_ctx"); server_context = (kadm5_server_context *)kadm_handle; slave_status(context, status_file, "creating log file"); ret = server_context->db->hdb_open(context, server_context->db, O_RDWR | O_CREAT, 0600); if (ret) krb5_err (context, 1, ret, "db->open"); ret = kadm5_log_init (server_context); if (ret) krb5_err (context, 1, ret, "kadm5_log_init"); ret = server_context->db->hdb_close (context, server_context->db); if (ret) krb5_err (context, 1, ret, "db->close"); get_creds(context, keytab_str, &ccache, master); ret = krb5_sname_to_principal (context, master, IPROP_NAME, KRB5_NT_SRV_HST, &server); if (ret) krb5_err (context, 1, ret, "krb5_sname_to_principal"); auth_context = NULL; master_fd = -1; krb5_appdefault_time(context, config_name, NULL, "reconnect-min", 10, &reconnect_min); krb5_appdefault_time(context, config_name, NULL, "reconnect-max", 300, &reconnect_max); krb5_appdefault_time(context, config_name, NULL, "reconnect-backoff", 10, &backoff); reconnect = reconnect_min; slave_status(context, status_file, "ipropd-slave started"); roken_detach_finish(NULL, daemon_child); restarter_fd = restarter(context, NULL); while (!exit_flag) { struct timeval to; time_t now, elapsed; fd_set readset; int connected = FALSE; #ifndef NO_LIMIT_FD_SETSIZE if (restarter_fd >= FD_SETSIZE) krb5_errx(context, IPROPD_RESTART, "fd too large"); #endif FD_ZERO(&readset); if (restarter_fd > -1) FD_SET(restarter_fd, &readset); now = time(NULL); elapsed = now - before; if (elapsed < reconnect) { time_t left = reconnect - elapsed; krb5_warnx(context, "sleeping %d seconds before " "retrying to connect", (int)left); to.tv_sec = left; to.tv_usec = 0; if (select(restarter_fd + 1, &readset, NULL, NULL, &to) == 1) { exit_flag = SIGTERM; continue; } } before = now; slave_status(context, status_file, "connecting to master: %s\n", master); master_fd = connect_to_master (context, master, port_str); if (master_fd < 0) goto retry; reconnect = reconnect_min; if (auth_context) { krb5_auth_con_free(context, auth_context); auth_context = NULL; krb5_cc_destroy(context, ccache); get_creds(context, keytab_str, &ccache, master); } if (verbose) krb5_warnx(context, "authenticating to master"); ret = krb5_sendauth (context, &auth_context, &master_fd, IPROP_VERSION, NULL, server, AP_OPTS_MUTUAL_REQUIRED, NULL, NULL, ccache, NULL, NULL, NULL); if (ret) { krb5_warn (context, ret, "krb5_sendauth"); goto retry; } krb5_warnx(context, "ipropd-slave started at version: %ld", (long)server_context->log_context.version); ret = ihave(context, auth_context, master_fd, server_context->log_context.version); if (ret) goto retry; connected = TRUE; if (verbose) krb5_warnx(context, "connected to master"); slave_status(context, status_file, "connected to master, waiting instructions"); while (connected && !exit_flag) { krb5_data out; krb5_storage *sp; uint32_t tmp; int max_fd; #ifndef NO_LIMIT_FD_SETSIZE if (master_fd >= FD_SETSIZE) krb5_errx(context, IPROPD_RESTART, "fd too large"); if (restarter_fd >= FD_SETSIZE) krb5_errx(context, IPROPD_RESTART, "fd too large"); max_fd = max(restarter_fd, master_fd); #endif FD_ZERO(&readset); FD_SET(master_fd, &readset); if (restarter_fd != -1) FD_SET(restarter_fd, &readset); to.tv_sec = time_before_lost; to.tv_usec = 0; ret = select (max_fd + 1, &readset, NULL, NULL, &to); if (ret < 0) { if (errno == EINTR) continue; else krb5_err (context, 1, errno, "select"); } if (ret == 0) { krb5_warnx(context, "server didn't send a message " "in %d seconds", time_before_lost); connected = FALSE; continue; } if (restarter_fd > -1 && FD_ISSET(restarter_fd, &readset)) { if (verbose) krb5_warnx(context, "slave restarter exited"); exit_flag = SIGTERM; } if (!FD_ISSET(master_fd, &readset)) continue; if (verbose) krb5_warnx(context, "message from master"); ret = krb5_read_priv_message(context, auth_context, &master_fd, &out); if (ret) { krb5_warn(context, ret, "krb5_read_priv_message"); connected = FALSE; continue; } sp = krb5_storage_from_mem (out.data, out.length); if (sp == NULL) krb5_err(context, IPROPD_RESTART, errno, "krb5_storage_from_mem"); ret = krb5_ret_uint32(sp, &tmp); if (ret == HEIM_ERR_EOF) { krb5_warn(context, ret, "master sent zero-length message"); connected = FALSE; continue; } if (ret != 0) { krb5_warn(context, ret, "couldn't read master's message"); connected = FALSE; continue; } ret = server_context->db->hdb_open(context, server_context->db, O_RDWR | O_CREAT, 0600); if (ret) krb5_err (context, 1, ret, "db->open while handling a " "message from the master"); ret = kadm5_log_init(server_context); if (ret) { krb5_err(context, IPROPD_RESTART, ret, "kadm5_log_init while " "handling a message from the master"); } ret = server_context->db->hdb_close (context, server_context->db); if (ret) krb5_err (context, 1, ret, "db->close while handling a " "message from the master"); switch (tmp) { case FOR_YOU : if (verbose) krb5_warnx(context, "master sent us diffs"); ret2 = receive(context, sp, server_context); if (ret2) krb5_warn(context, ret2, "receive from ipropd-master had errors"); ret = ihave(context, auth_context, master_fd, server_context->log_context.version); if (ret || ret2) connected = FALSE; /* * If it returns an error, receive() may nonetheless * have committed some entries successfully, so we must * update the slave_status even if there were errors. */ is_up_to_date(context, status_file, server_context); break; case TELL_YOU_EVERYTHING : if (verbose) krb5_warnx(context, "master sent us a full dump"); ret = receive_everything(context, master_fd, server_context, auth_context); if (ret == 0) { ret = ihave(context, auth_context, master_fd, server_context->log_context.version); } if (ret) connected = FALSE; else is_up_to_date(context, status_file, server_context); break; case ARE_YOU_THERE : if (verbose) krb5_warnx(context, "master sent us a ping"); is_up_to_date(context, status_file, server_context); ret = ihave(context, auth_context, master_fd, server_context->log_context.version); if (ret) connected = FALSE; send_im_here(context, master_fd, auth_context); break; case YOU_HAVE_LAST_VERSION: if (verbose) krb5_warnx(context, "master tells us we are up to date"); is_up_to_date(context, status_file, server_context); break; case NOW_YOU_HAVE : case I_HAVE : case ONE_PRINC : case I_AM_HERE : default : krb5_warnx (context, "Ignoring command %d", tmp); break; } krb5_storage_free (sp); krb5_data_free (&out); } slave_status(context, status_file, "disconnected from master"); retry: if (connected == FALSE) krb5_warnx (context, "disconnected for server"); if (exit_flag) krb5_warnx (context, "got an exit signal"); if (master_fd >= 0) close(master_fd); reconnect += backoff; if (reconnect > reconnect_max) { slave_status(context, status_file, "disconnected from master for a long time"); reconnect = reconnect_max; } } if (status_file) { /* XXX It'd be better to leave it saying we're not here */ unlink(status_file); } if (0); #ifndef NO_SIGXCPU else if(exit_flag == SIGXCPU) krb5_warnx(context, "%s CPU time limit exceeded", getprogname()); #endif else if(exit_flag == SIGINT || exit_flag == SIGTERM) krb5_warnx(context, "%s terminated", getprogname()); else krb5_warnx(context, "%s unexpected exit reason: %ld", getprogname(), (long)exit_flag); return 0; }
static int append_to_log_file(krb5_context context, kadm5_server_context *server_context, krb5_storage *sp, off_t start, ssize_t slen) { size_t len; ssize_t sret; off_t log_off; int ret, ret2; void *buf; if (verbose) krb5_warnx(context, "appending diffs to log"); if (slen == 0) return 0; if (slen < 0) return EINVAL; len = slen; if (len != slen) return EOVERFLOW; buf = malloc(len); if (buf == NULL && len != 0) { krb5_warn(context, errno, "malloc: no memory"); return ENOMEM; } if (krb5_storage_seek(sp, start, SEEK_SET) != start) { krb5_errx(context, IPROPD_RESTART, "krb5_storage_seek() failed"); /* can't happen */ } sret = krb5_storage_read(sp, buf, len); if (sret < 0) return errno; if (len != (size_t)sret) { /* Can't happen */ krb5_errx(context, IPROPD_RESTART, "short krb5_storage_read() from memory buffer"); } log_off = lseek(server_context->log_context.log_fd, 0, SEEK_CUR); if (log_off == -1) return errno; /* * Use net_write() so we get an errno if less that len bytes were * written. */ sret = net_write(server_context->log_context.log_fd, buf, len); free(buf); if (sret != slen) ret = errno; else ret = fsync(server_context->log_context.log_fd); if (ret == 0) return 0; /* * Attempt to recover from this. First, truncate the log file * and reset the fd offset. Failure to do this -> unlink the * log file and re-create it. Since we're the slave, we ought to be * able to recover from the log being unlinked... */ if (ftruncate(server_context->log_context.log_fd, log_off) == -1 || lseek(server_context->log_context.log_fd, log_off, SEEK_SET) == -1) { (void) kadm5_log_end(server_context); if (unlink(server_context->log_context.log_file) == -1) { krb5_err(context, IPROPD_FATAL, errno, "Failed to recover from failure to write log " "entries from master to disk"); } ret2 = kadm5_log_init(server_context); if (ret2) { krb5_err(context, IPROPD_RESTART_SLOW, ret2, "Failed to initialize log to recover from " "failure to write log entries from master to disk"); } } if (ret == ENOSPC || ret == EDQUOT || ret == EFBIG) { /* Unlink the file in these cases. */ krb5_warn(context, IPROPD_RESTART_SLOW, "Failed to write log entries from master to disk"); (void) kadm5_log_end(server_context); if (unlink(server_context->log_context.log_file) == -1) { krb5_err(context, IPROPD_FATAL, errno, "Failed to recover from failure to write log " "entries from master to disk"); } ret2 = kadm5_log_init(server_context); if (ret2) { krb5_err(context, IPROPD_RESTART_SLOW, ret2, "Failed to initialize log to recover from " "failure to write log entries from master to disk"); } return ret; } /* * All other errors we treat as fatal here. This includes, for * example, EIO and EPIPE (sorry, can't log to pipes nor sockets). */ krb5_err(context, IPROPD_FATAL, ret, "Failed to write log entries from master to disk"); }
kadm5_ret_t kadm5_s_rename_principal(void *server_handle, krb5_principal source, krb5_principal target) { kadm5_server_context *context = server_handle; kadm5_ret_t ret; hdb_entry_ex ent; krb5_principal oldname; memset(&ent, 0, sizeof(ent)); if (krb5_principal_compare(context->context, source, target)) return KADM5_DUP; /* XXX is this right? */ if (!context->keep_open) { ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0); if(ret) return ret; } ret = kadm5_log_init(context); if (ret) goto out; ret = context->db->hdb_fetch_kvno(context->context, context->db, source, HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent); if (ret) goto out2; oldname = ent.entry.principal; ret = _kadm5_set_modifier(context, &ent.entry); if (ret) goto out3; { /* fix salt */ size_t i; Salt salt; krb5_salt salt2; memset(&salt, 0, sizeof(salt)); krb5_get_pw_salt(context->context, source, &salt2); salt.type = hdb_pw_salt; salt.salt = salt2.saltvalue; for(i = 0; i < ent.entry.keys.len; i++){ if(ent.entry.keys.val[i].salt == NULL){ ent.entry.keys.val[i].salt = malloc(sizeof(*ent.entry.keys.val[i].salt)); if (ent.entry.keys.val[i].salt == NULL) ret = ENOMEM; else ret = copy_Salt(&salt, ent.entry.keys.val[i].salt); if (ret) break; } } krb5_free_salt(context->context, salt2); } if (ret) goto out3; /* Borrow target */ ent.entry.principal = target; ret = hdb_seal_keys(context->context, context->db, &ent.entry); if (ret) goto out3; /* This logs the change for iprop and writes to the HDB */ ret = kadm5_log_rename(context, source, &ent.entry); out3: ent.entry.principal = oldname; /* Unborrow target */ hdb_free_entry(context->context, &ent); out2: (void) kadm5_log_end(context); out: if (!context->keep_open) { kadm5_ret_t ret2; ret2 = context->db->hdb_close(context->context, context->db); if (ret == 0 && ret2 != 0) ret = ret2; } return _kadm5_error_code(ret); }
static int doit(const char *filename, int mergep) { krb5_error_code ret = 0; FILE *f; char s[8192]; /* XXX should fix this properly */ char *p; int line; int flags = O_RDWR; struct entry e; hdb_entry_ex ent; HDB *db = _kadm5_s_get_db(kadm_handle); f = fopen(filename, "r"); if(f == NULL){ krb5_warn(context, errno, "fopen(%s)", filename); return 1; } /* * We don't have a version number in the dump, so we don't know which iprop * log entries to keep, if any. We throw the log away. * * We could merge the ipropd-master/slave dump/load here as an option, in * which case we would first load the dump. * * If we're merging, first recover unconfirmed records in the existing log. */ if (mergep) ret = kadm5_log_init(kadm_handle); if (ret == 0) ret = kadm5_log_reinit(kadm_handle, 0); if (ret) { fclose (f); krb5_warn(context, ret, "kadm5_log_reinit"); return 1; } if(!mergep) flags |= O_CREAT | O_TRUNC; ret = db->hdb_open(context, db, flags, 0600); if(ret){ krb5_warn(context, ret, "hdb_open"); fclose(f); return 1; } line = 0; ret = 0; while(fgets(s, sizeof(s), f) != NULL) { line++; p = s; while (isspace((unsigned char)*p)) p++; e.principal = p; for(p = s; *p; p++){ if(*p == '\\') p++; else if(isspace((unsigned char)*p)) { *p = 0; break; } } p = skip_next(p); e.key = p; p = skip_next(p); e.created = p; p = skip_next(p); e.modified = p; p = skip_next(p); e.valid_start = p; p = skip_next(p); e.valid_end = p; p = skip_next(p); e.pw_end = p; p = skip_next(p); e.max_life = p; p = skip_next(p); e.max_renew = p; p = skip_next(p); e.flags = p; p = skip_next(p); e.generation = p; p = skip_next(p); e.extensions = p; skip_next(p); memset(&ent, 0, sizeof(ent)); ret = krb5_parse_name(context, e.principal, &ent.entry.principal); if(ret) { const char *msg = krb5_get_error_message(context, ret); fprintf(stderr, "%s:%d:%s (%s)\n", filename, line, msg, e.principal); krb5_free_error_message(context, msg); continue; } if (parse_keys(&ent.entry, e.key)) { fprintf (stderr, "%s:%d:error parsing keys (%s)\n", filename, line, e.key); hdb_free_entry (context, &ent); continue; } if (parse_event(&ent.entry.created_by, e.created) == -1) { fprintf (stderr, "%s:%d:error parsing created event (%s)\n", filename, line, e.created); hdb_free_entry (context, &ent); continue; } if (parse_event_alloc (&ent.entry.modified_by, e.modified) == -1) { fprintf (stderr, "%s:%d:error parsing event (%s)\n", filename, line, e.modified); hdb_free_entry (context, &ent); continue; } if (parse_time_string_alloc (&ent.entry.valid_start, e.valid_start) == -1) { fprintf (stderr, "%s:%d:error parsing time (%s)\n", filename, line, e.valid_start); hdb_free_entry (context, &ent); continue; } if (parse_time_string_alloc (&ent.entry.valid_end, e.valid_end) == -1) { fprintf (stderr, "%s:%d:error parsing time (%s)\n", filename, line, e.valid_end); hdb_free_entry (context, &ent); continue; } if (parse_time_string_alloc (&ent.entry.pw_end, e.pw_end) == -1) { fprintf (stderr, "%s:%d:error parsing time (%s)\n", filename, line, e.pw_end); hdb_free_entry (context, &ent); continue; } if (parse_integer_alloc (&ent.entry.max_life, e.max_life) == -1) { fprintf (stderr, "%s:%d:error parsing lifetime (%s)\n", filename, line, e.max_life); hdb_free_entry (context, &ent); continue; } if (parse_integer_alloc (&ent.entry.max_renew, e.max_renew) == -1) { fprintf (stderr, "%s:%d:error parsing lifetime (%s)\n", filename, line, e.max_renew); hdb_free_entry (context, &ent); continue; } if (parse_hdbflags2int (&ent.entry.flags, e.flags) != 1) { fprintf (stderr, "%s:%d:error parsing flags (%s)\n", filename, line, e.flags); hdb_free_entry (context, &ent); continue; } if(parse_generation(e.generation, &ent.entry.generation) == -1) { fprintf (stderr, "%s:%d:error parsing generation (%s)\n", filename, line, e.generation); hdb_free_entry (context, &ent); continue; } if(parse_extensions(e.extensions, &ent.entry.extensions) == -1) { fprintf (stderr, "%s:%d:error parsing extension (%s)\n", filename, line, e.extensions); hdb_free_entry (context, &ent); continue; } ret = db->hdb_store(context, db, HDB_F_REPLACE, &ent); hdb_free_entry (context, &ent); if (ret) { krb5_warn(context, ret, "db_store"); break; } } (void) kadm5_log_end(kadm_handle); db->hdb_close(context, db); fclose(f); return ret != 0; }