static void setup_env (krb5_context context, krb5_keytab *kt) { krb5_error_code ret; if (keytab_file) ret = krb5_kt_resolve (context, keytab_file, kt); else ret = krb5_kt_default (context, kt); if (ret) krb5_err (context, 1, ret, "resolving keytab"); if (client_principal_str == NULL) krb5_errx (context, 1, "missing client principal"); ret = krb5_parse_name (context, client_principal_str, &client_principal); if (ret) krb5_err (context, 1, ret, "resolvning client name"); if (server_principal_str == NULL) krb5_errx (context, 1, "missing server principal"); ret = krb5_parse_name (context, server_principal_str, &server_principal); if (ret) krb5_err (context, 1, ret, "resolvning client name"); if (ticket_flags_str) { int ticket_flags_int; ticket_flags_int = parse_flags(ticket_flags_str, asn1_TicketFlags_units(), 0); if (ticket_flags_int <= 0) { krb5_warnx (context, "bad ticket flags: `%s'", ticket_flags_str); print_flags_table (asn1_TicketFlags_units(), stderr); exit (1); } if (ticket_flags_int) ticket_flags = int2TicketFlags (ticket_flags_int); } }
int dump(struct dump_options *opt, int argc, char **argv) { krb5_error_code ret; FILE *f; HDB *db = NULL; if(!local_flag) { krb5_warnx(context, "dump is only available in local (-l) mode"); return 0; } db = _kadm5_s_get_db(kadm_handle); if(argc == 0) f = stdout; else f = fopen(argv[0], "w"); if(f == NULL) { krb5_warn(context, errno, "open: %s", argv[0]); goto out; } ret = db->hdb_open(context, db, O_RDONLY, 0600); if(ret) { krb5_warn(context, ret, "hdb_open"); goto out; } hdb_foreach(context, db, opt->decrypt_flag ? HDB_F_DECRYPT : 0, hdb_print_entry, f); db->hdb_close(context, db); out: if(f && f != stdout) fclose(f); return 0; }
/** * A wrapper around sqlite3_exec. * * @param context The current krb5 context * @param database An open sqlite3 database handle * @param statement SQL code to execute * @param error_code What to return if the statement fails * * @return 0 if OK, else error_code */ static krb5_error_code hdb_sqlite_exec_stmt(krb5_context context, hdb_sqlite_db *hsdb, const char *statement, krb5_error_code error_code) { int ret; int reinit_stmts = 0; sqlite3 *database = hsdb->db; ret = sqlite3_exec(database, statement, NULL, NULL, NULL); while(((ret == SQLITE_BUSY) || (ret == SQLITE_IOERR_BLOCKED) || (ret == SQLITE_LOCKED))) { if (reinit_stmts == 0 && ret == SQLITE_BUSY) { finalize_stmts(context, hsdb); reinit_stmts = 1; } krb5_warnx(context, "hdb-sqlite: exec busy: %d", (int)getpid()); sleep(1); ret = sqlite3_exec(database, statement, NULL, NULL, NULL); } if (ret != SQLITE_OK && error_code) { krb5_set_error_message(context, error_code, "Execute %s: %s", statement, sqlite3_errmsg(database)); return error_code; } if (reinit_stmts) return prep_stmts(context, hsdb); return 0; }
int password_quality(void *opt, int argc, char **argv) { krb5_error_code ret; krb5_principal principal; krb5_data pw_data; const char *s; ret = krb5_parse_name(context, argv[0], &principal); if(ret) { krb5_warn(context, ret, "krb5_parse_name(%s)", argv[0]); return 0; } pw_data.data = argv[1]; pw_data.length = strlen(argv[1]); s = kadm5_check_password_quality (context, principal, &pw_data); if (s) krb5_warnx(context, "kadm5_check_password_quality: %s", s); krb5_free_principal(context, principal); return 0; }
static krb5_error_code get_new_tickets(krb5_context context, krb5_principal principal, krb5_ccache ccache, krb5_deltat ticket_life, int interactive) { krb5_error_code ret; krb5_creds cred; char passwd[256]; krb5_deltat start_time = 0; krb5_deltat renew = 0; const char *renewstr = NULL; krb5_enctype *enctype = NULL; krb5_ccache tempccache = NULL; krb5_init_creds_context ctx = NULL; krb5_get_init_creds_opt *opt = NULL; krb5_prompter_fct prompter = krb5_prompter_posix; #ifndef NO_NTLM struct ntlm_buf ntlmkey; memset(&ntlmkey, 0, sizeof(ntlmkey)); #endif passwd[0] = '\0'; if (!interactive) prompter = NULL; if (password_file) { FILE *f; if (strcasecmp("STDIN", password_file) == 0) f = stdin; else f = fopen(password_file, "r"); if (f == NULL) { krb5_warnx(context, "Failed to open the password file %s", password_file); return errno; } if (fgets(passwd, sizeof(passwd), f) == NULL) { krb5_warnx(context, N_("Failed to read password from file %s", ""), password_file); fclose(f); return EINVAL; /* XXX Need a better error */ } if (f != stdin) fclose(f); passwd[strcspn(passwd, "\n")] = '\0'; } #ifdef __APPLE__ if (passwd[0] == '\0') { const char *realm; OSStatus osret; UInt32 length; void *buffer; char *name; realm = krb5_principal_get_realm(context, principal); ret = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name); if (ret) goto nopassword; osret = SecKeychainFindGenericPassword(NULL, strlen(realm), realm, strlen(name), name, &length, &buffer, NULL); free(name); if (osret == noErr && length < sizeof(passwd) - 1) { memcpy(passwd, buffer, length); passwd[length] = '\0'; } nopassword: do { } while(0); } #endif memset(&cred, 0, sizeof(cred)); ret = krb5_get_init_creds_opt_alloc(context, &opt); if (ret) { krb5_warn(context, ret, "krb5_get_init_creds_opt_alloc"); goto out; } krb5_get_init_creds_opt_set_default_flags(context, "kinit", krb5_principal_get_realm(context, principal), opt); if (forwardable_flag != -1) krb5_get_init_creds_opt_set_forwardable(opt, forwardable_flag); if (proxiable_flag != -1) krb5_get_init_creds_opt_set_proxiable(opt, proxiable_flag); if (anonymous_flag) krb5_get_init_creds_opt_set_anonymous(opt, anonymous_flag); if (pac_flag != -1) krb5_get_init_creds_opt_set_pac_request(context, opt, pac_flag ? TRUE : FALSE); if (canonicalize_flag) krb5_get_init_creds_opt_set_canonicalize(context, opt, TRUE); if (pk_enterprise_flag || enterprise_flag || canonicalize_flag || windows_flag) krb5_get_init_creds_opt_set_win2k(context, opt, TRUE); if (pk_user_id || ent_user_id || anonymous_flag) { ret = krb5_get_init_creds_opt_set_pkinit(context, opt, principal, pk_user_id, pk_x509_anchors, NULL, NULL, pk_use_enckey ? 2 : 0 | anonymous_flag ? 4 : 0, prompter, NULL, passwd); if (ret) { krb5_warn(context, ret, "krb5_get_init_creds_opt_set_pkinit"); goto out; } if (ent_user_id) krb5_get_init_creds_opt_set_pkinit_user_certs(context, opt, ent_user_id); } if (addrs_flag != -1) krb5_get_init_creds_opt_set_addressless(context, opt, addrs_flag ? FALSE : TRUE); if (renew_life == NULL && renewable_flag) renewstr = "1 month"; if (renew_life) renewstr = renew_life; if (renewstr) { renew = parse_time(renewstr, "s"); if (renew < 0) errx(1, "unparsable time: %s", renewstr); krb5_get_init_creds_opt_set_renew_life(opt, renew); } if (ticket_life != 0) krb5_get_init_creds_opt_set_tkt_life(opt, ticket_life); if (start_str) { int tmp = parse_time(start_str, "s"); if (tmp < 0) errx(1, N_("unparsable time: %s", ""), start_str); start_time = tmp; } if (etype_str.num_strings) { int i; enctype = malloc(etype_str.num_strings * sizeof(*enctype)); if (enctype == NULL) errx(1, "out of memory"); for(i = 0; i < etype_str.num_strings; i++) { ret = krb5_string_to_enctype(context, etype_str.strings[i], &enctype[i]); if (ret) errx(1, "unrecognized enctype: %s", etype_str.strings[i]); } krb5_get_init_creds_opt_set_etype_list(opt, enctype, etype_str.num_strings); } ret = krb5_init_creds_init(context, principal, prompter, NULL, start_time, opt, &ctx); if (ret) { krb5_warn(context, ret, "krb5_init_creds_init"); goto out; } if (server_str) { ret = krb5_init_creds_set_service(context, ctx, server_str); if (ret) { krb5_warn(context, ret, "krb5_init_creds_set_service"); goto out; } } if (fast_armor_cache_string) { krb5_ccache fastid; ret = krb5_cc_resolve(context, fast_armor_cache_string, &fastid); if (ret) { krb5_warn(context, ret, "krb5_cc_resolve(FAST cache)"); goto out; } ret = krb5_init_creds_set_fast_ccache(context, ctx, fastid); if (ret) { krb5_warn(context, ret, "krb5_init_creds_set_fast_ccache"); goto out; } } if (use_keytab || keytab_str) { ret = krb5_init_creds_set_keytab(context, ctx, kt); if (ret) { krb5_warn(context, ret, "krb5_init_creds_set_keytab"); goto out; } } else if (pk_user_id || ent_user_id || anonymous_flag) { } else if (!interactive && passwd[0] == '\0') { static int already_warned = 0; if (!already_warned) krb5_warnx(context, "Not interactive, failed to get " "initial ticket"); krb5_get_init_creds_opt_free(context, opt); already_warned = 1; return 0; } else { if (passwd[0] == '\0') { char *p, *prompt; int aret = 0; ret = krb5_unparse_name(context, principal, &p); if (ret) errx(1, "failed to generate passwd prompt: not enough memory"); aret = asprintf(&prompt, N_("%s's Password: "******""), p); free(p); if (aret == -1) errx(1, "failed to generate passwd prompt: not enough memory"); if (UI_UTIL_read_pw_string(passwd, sizeof(passwd)-1, prompt, 0)){ memset(passwd, 0, sizeof(passwd)); errx(1, "failed to read password"); } free(prompt); } if (passwd[0]) { ret = krb5_init_creds_set_password(context, ctx, passwd); if (ret) { krb5_warn(context, ret, "krb5_init_creds_set_password"); goto out; } } } ret = krb5_init_creds_get(context, ctx); #ifndef NO_NTLM if (ntlm_domain && passwd[0]) heim_ntlm_nt_key(passwd, &ntlmkey); #endif memset(passwd, 0, sizeof(passwd)); switch(ret){ case 0: break; case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */ exit(1); case KRB5KRB_AP_ERR_BAD_INTEGRITY: case KRB5KRB_AP_ERR_MODIFIED: case KRB5KDC_ERR_PREAUTH_FAILED: case KRB5_GET_IN_TKT_LOOP: krb5_warnx(context, N_("Password incorrect", "")); goto out; case KRB5KRB_AP_ERR_V4_REPLY: krb5_warnx(context, N_("Looks like a Kerberos 4 reply", "")); goto out; case KRB5KDC_ERR_KEY_EXPIRED: krb5_warnx(context, N_("Password expired", "")); goto out; default: krb5_warn(context, ret, "krb5_get_init_creds"); goto out; } krb5_process_last_request(context, opt, ctx); ret = krb5_init_creds_get_creds(context, ctx, &cred); if (ret) { krb5_warn(context, ret, "krb5_init_creds_get_creds"); goto out; } if (ticket_life != 0) { if (abs(cred.times.endtime - cred.times.starttime - ticket_life) > 30) { char life[64]; unparse_time_approx(cred.times.endtime - cred.times.starttime, life, sizeof(life)); krb5_warnx(context, N_("NOTICE: ticket lifetime is %s", ""), life); } } if (renew_life) { if (abs(cred.times.renew_till - cred.times.starttime - renew) > 30) { char life[64]; unparse_time_approx(cred.times.renew_till - cred.times.starttime, life, sizeof(life)); krb5_warnx(context, N_("NOTICE: ticket renewable lifetime is %s", ""), life); } } krb5_free_cred_contents(context, &cred); ret = krb5_cc_new_unique(context, krb5_cc_get_type(context, ccache), NULL, &tempccache); if (ret) { krb5_warn(context, ret, "krb5_cc_new_unique"); goto out; } ret = krb5_init_creds_store(context, ctx, tempccache); if (ret) { krb5_warn(context, ret, "krb5_init_creds_store"); goto out; } krb5_init_creds_free(context, ctx); ctx = NULL; ret = krb5_cc_move(context, tempccache, ccache); if (ret) { krb5_warn(context, ret, "krb5_cc_move"); goto out; } tempccache = NULL; if (switch_cache_flags) krb5_cc_switch(context, ccache); #ifndef NO_NTLM if (ntlm_domain && ntlmkey.data) store_ntlmkey(context, ccache, ntlm_domain, &ntlmkey); #endif if (ok_as_delegate_flag || windows_flag || use_referrals_flag) { unsigned char d = 0; krb5_data data; if (ok_as_delegate_flag || windows_flag) d |= 1; if (use_referrals_flag || windows_flag) d |= 2; data.length = 1; data.data = &d; krb5_cc_set_config(context, ccache, NULL, "realm-config", &data); } out: krb5_get_init_creds_opt_free(context, opt); if (ctx) krb5_init_creds_free(context, ctx); if (tempccache) krb5_cc_close(context, tempccache); if (enctype) free(enctype); return ret; }
void start_server(krb5_context context, const char *port_str) { int e; struct kadm_port *p; krb5_socket_t *socks = NULL, *tmp; unsigned int num_socks = 0; int i; if (port_str == NULL) port_str = "+"; parse_ports(context, port_str); for(p = kadm_ports; p; p = p->next) { struct addrinfo hints, *ai, *ap; char portstr[32]; memset (&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; hints.ai_socktype = SOCK_STREAM; e = getaddrinfo(NULL, p->port, &hints, &ai); if(e) { snprintf(portstr, sizeof(portstr), "%u", p->def_port); e = getaddrinfo(NULL, portstr, &hints, &ai); } if(e) { krb5_warn(context, krb5_eai_to_heim_errno(e, errno), "%s", portstr); continue; } i = 0; for(ap = ai; ap; ap = ap->ai_next) i++; tmp = realloc(socks, (num_socks + i) * sizeof(*socks)); if(tmp == NULL) { krb5_warnx(context, "failed to reallocate %lu bytes", (unsigned long)(num_socks + i) * sizeof(*socks)); continue; } socks = tmp; for(ap = ai; ap; ap = ap->ai_next) { krb5_socket_t s = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol); if(rk_IS_BAD_SOCKET(s)) { krb5_warn(context, rk_SOCK_ERRNO, "socket"); continue; } socket_set_reuseaddr(s, 1); socket_set_ipv6only(s, 1); if (rk_IS_SOCKET_ERROR(bind (s, ap->ai_addr, ap->ai_addrlen))) { krb5_warn(context, rk_SOCK_ERRNO, "bind"); rk_closesocket(s); continue; } if (rk_IS_SOCKET_ERROR(listen (s, SOMAXCONN))) { krb5_warn(context, rk_SOCK_ERRNO, "listen"); rk_closesocket(s); continue; } socks[num_socks++] = s; } freeaddrinfo (ai); } if(num_socks == 0) krb5_errx(context, 1, "no sockets to listen to - exiting"); wait_for_connection(context, socks, num_socks); }
/** * Stores an hdb_entry in the database. If flags contains HDB_F_REPLACE * a previous entry may be replaced. * * @param context The current krb5_context * @param db Heimdal database handle * @param flags May currently only contain HDB_F_REPLACE * @param entry The data to store * * @return 0 if everything worked, an error code if not */ static krb5_error_code hdb_sqlite_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) { int ret; int i; sqlite_int64 entry_id; char *principal_string = NULL; char *alias_string; const HDB_Ext_Aliases *aliases; hdb_sqlite_db *hsdb = (hdb_sqlite_db *)(db->hdb_db); krb5_data value; sqlite3_stmt *get_ids = hsdb->get_ids; ret = hdb_sqlite_exec_stmt(context, hsdb->db, "BEGIN IMMEDIATE TRANSACTION", EINVAL); if(ret != SQLITE_OK) { krb5_set_error_string(context, "SQLite BEGIN TRANSACTION failed: %s", sqlite3_errmsg(hsdb->db)); goto rollback; } ret = krb5_unparse_name(context, entry->entry.principal, &principal_string); if (ret) { goto rollback; } ret = hdb_seal_keys(context, db, &entry->entry); if(ret) { goto rollback; } ret = hdb_entry2value(context, &entry->entry, &value); if(ret) { goto rollback; } sqlite3_bind_text(get_ids, 1, principal_string, -1, SQLITE_STATIC); ret = hdb_sqlite_step(context, hsdb->db, get_ids); if(ret == SQLITE_DONE) { /* No such principal */ sqlite3_bind_blob(hsdb->add_entry, 1, value.data, value.length, SQLITE_STATIC); ret = hdb_sqlite_step(context, hsdb->db, hsdb->add_entry); sqlite3_clear_bindings(hsdb->add_entry); sqlite3_reset(hsdb->add_entry); if(ret != SQLITE_DONE) goto rollback; sqlite3_bind_text(hsdb->add_principal, 1, principal_string, -1, SQLITE_STATIC); ret = hdb_sqlite_step(context, hsdb->db, hsdb->add_principal); sqlite3_clear_bindings(hsdb->add_principal); sqlite3_reset(hsdb->add_principal); if(ret != SQLITE_DONE) goto rollback; entry_id = sqlite3_column_int64(get_ids, 1); } else if(ret == SQLITE_ROW) { /* Found a principal */ if(! (flags & HDB_F_REPLACE)) /* Not allowed to replace it */ goto rollback; entry_id = sqlite3_column_int64(get_ids, 1); sqlite3_bind_int64(hsdb->delete_aliases, 1, entry_id); ret = hdb_sqlite_step_once(context, db, hsdb->delete_aliases); if(ret != SQLITE_DONE) goto rollback; sqlite3_bind_blob(hsdb->update_entry, 1, value.data, value.length, SQLITE_STATIC); sqlite3_bind_int64(hsdb->update_entry, 2, entry_id); ret = hdb_sqlite_step_once(context, db, hsdb->update_entry); if(ret != SQLITE_DONE) goto rollback; } else { /* Error! */ goto rollback; } ret = hdb_entry_get_aliases(&entry->entry, &aliases); if(ret || aliases == NULL) goto commit; for(i = 0; i < aliases->aliases.len; i++) { ret = krb5_unparse_name(context, &aliases->aliases.val[i], &alias_string); if (ret) { free(alias_string); goto rollback; } sqlite3_bind_text(hsdb->add_alias, 1, alias_string, -1, SQLITE_STATIC); sqlite3_bind_int64(hsdb->add_alias, 2, entry_id); ret = hdb_sqlite_step_once(context, db, hsdb->add_alias); free(alias_string); if(ret != SQLITE_DONE) goto rollback; } ret = 0; commit: free(principal_string); krb5_data_free(&value); sqlite3_clear_bindings(get_ids); sqlite3_reset(get_ids); ret = hdb_sqlite_exec_stmt(context, hsdb->db, "COMMIT", EINVAL); if(ret != SQLITE_OK) krb5_warnx(context, "hdb-sqlite: COMMIT problem: %d: %s", ret, sqlite3_errmsg(hsdb->db)); return ret; rollback: krb5_warnx(context, "hdb-sqlite: store rollback problem: %d: %s", ret, sqlite3_errmsg(hsdb->db)); free(principal_string); ret = hdb_sqlite_exec_stmt(context, hsdb->db, "ROLLBACK", EINVAL); return ret; }
static int send_diffs (krb5_context context, slave *s, int log_fd, const char *database, u_int32_t current_version) { krb5_storage *sp; u_int32_t ver; time_t timestamp; enum kadm_ops op; u_int32_t len; off_t right, left; krb5_data data; int ret = 0; if (s->version == current_version) return 0; if (s->flags & SLAVE_F_DEAD) return 0; sp = kadm5_log_goto_end (log_fd); right = krb5_storage_seek(sp, 0, SEEK_CUR); for (;;) { if (kadm5_log_previous (sp, &ver, ×tamp, &op, &len)) abort (); left = krb5_storage_seek(sp, -16, SEEK_CUR); if (ver == s->version) return 0; if (ver == s->version + 1) break; if (left == 0) return send_complete (context, s, database, current_version); } ret = krb5_data_alloc (&data, right - left + 4); if (ret) { krb5_warn (context, ret, "send_diffs: krb5_data_alloc"); slave_dead(s); return 1; } krb5_storage_read (sp, (char *)data.data + 4, data.length - 4); krb5_storage_free(sp); sp = krb5_storage_from_data (&data); if (sp == NULL) { krb5_warnx (context, "send_diffs: krb5_storage_from_data"); slave_dead(s); return 1; } krb5_store_int32 (sp, FOR_YOU); krb5_storage_free(sp); ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); krb5_data_free(&data); if (ret) { krb5_warn (context, ret, "send_diffs: krb5_write_priv_message"); slave_dead(s); return 1; } slave_seen(s); return 0; }
int main(int argc, char **argv) { krb5_error_code ret; krb5_context context; krb5_auth_context ac = NULL; krb5_principal c1, c2; krb5_authenticator authent; krb5_keytab keytab; krb5_socket_t sock = rk_INVALID_SOCKET; HDB *db = NULL; int optidx = 0; char *tmp_db; krb5_log_facility *fac; int nprincs; setprogname(argv[0]); ret = krb5_init_context(&context); if(ret) exit(1); ret = krb5_openlog(context, "hpropd", &fac); if(ret) errx(1, "krb5_openlog"); krb5_set_warn_dest(context, fac); if(getarg(args, num_args, argc, argv, &optidx)) usage(1); if(local_realm != NULL) krb5_set_default_realm(context, local_realm); if(help_flag) usage(0); if(version_flag) { print_version(NULL); exit(0); } argc -= optidx; argv += optidx; if (argc != 0) usage(1); if (database == NULL) database = hdb_default_db(context); if(from_stdin) { sock = STDIN_FILENO; } else { struct sockaddr_storage ss; struct sockaddr *sa = (struct sockaddr *)&ss; socklen_t sin_len = sizeof(ss); char addr_name[256]; krb5_ticket *ticket; char *server; sock = STDIN_FILENO; #ifdef SUPPORT_INETD if (inetd_flag == -1) { if (getpeername (sock, sa, &sin_len) < 0) { inetd_flag = 0; } else { inetd_flag = 1; } } #else inetd_flag = 0; #endif if (!inetd_flag) { mini_inetd (krb5_getportbyname (context, "hprop", "tcp", HPROP_PORT), &sock); } sin_len = sizeof(ss); if(getpeername(sock, sa, &sin_len) < 0) krb5_err(context, 1, errno, "getpeername"); if (inet_ntop(sa->sa_family, socket_get_address (sa), addr_name, sizeof(addr_name)) == NULL) strlcpy (addr_name, "unknown address", sizeof(addr_name)); krb5_log(context, fac, 0, "Connection from %s", addr_name); ret = krb5_kt_register(context, &hdb_kt_ops); if(ret) krb5_err(context, 1, ret, "krb5_kt_register"); if (ktname != NULL) { ret = krb5_kt_resolve(context, ktname, &keytab); if (ret) krb5_err (context, 1, ret, "krb5_kt_resolve %s", ktname); } else { ret = krb5_kt_default (context, &keytab); if (ret) krb5_err (context, 1, ret, "krb5_kt_default"); } ret = krb5_recvauth(context, &ac, &sock, HPROP_VERSION, NULL, 0, keytab, &ticket); if(ret) krb5_err(context, 1, ret, "krb5_recvauth"); ret = krb5_unparse_name(context, ticket->server, &server); if (ret) krb5_err(context, 1, ret, "krb5_unparse_name"); if (strncmp(server, "hprop/", 5) != 0) krb5_errx(context, 1, "ticket not for hprop (%s)", server); free(server); krb5_free_ticket (context, ticket); ret = krb5_auth_con_getauthenticator(context, ac, &authent); if(ret) krb5_err(context, 1, ret, "krb5_auth_con_getauthenticator"); ret = krb5_make_principal(context, &c1, NULL, "kadmin", "hprop", NULL); if(ret) krb5_err(context, 1, ret, "krb5_make_principal"); _krb5_principalname2krb5_principal(context, &c2, authent->cname, authent->crealm); if(!krb5_principal_compare(context, c1, c2)) { char *s; ret = krb5_unparse_name(context, c2, &s); if (ret) s = unparseable_name; krb5_errx(context, 1, "Unauthorized connection from %s", s); } krb5_free_principal(context, c1); krb5_free_principal(context, c2); ret = krb5_kt_close(context, keytab); if(ret) krb5_err(context, 1, ret, "krb5_kt_close"); } if(!print_dump) { asprintf(&tmp_db, "%s~", database); ret = hdb_create(context, &db, tmp_db); if(ret) krb5_err(context, 1, ret, "hdb_create(%s)", tmp_db); ret = db->hdb_open(context, db, O_RDWR | O_CREAT | O_TRUNC, 0600); if(ret) krb5_err(context, 1, ret, "hdb_open(%s)", tmp_db); } nprincs = 0; while(1){ krb5_data data; hdb_entry_ex entry; if(from_stdin) { ret = krb5_read_message(context, &sock, &data); if(ret != 0 && ret != HEIM_ERR_EOF) krb5_err(context, 1, ret, "krb5_read_message"); } else { ret = krb5_read_priv_message(context, ac, &sock, &data); if(ret) krb5_err(context, 1, ret, "krb5_read_priv_message"); } if(ret == HEIM_ERR_EOF || data.length == 0) { if(!from_stdin) { data.data = NULL; data.length = 0; krb5_write_priv_message(context, ac, &sock, &data); } if(!print_dump) { ret = db->hdb_close(context, db); if(ret) krb5_err(context, 1, ret, "db_close"); ret = db->hdb_rename(context, db, database); if(ret) krb5_err(context, 1, ret, "db_rename"); } break; } memset(&entry, 0, sizeof(entry)); ret = hdb_value2entry(context, &data, &entry.entry); krb5_data_free(&data); if(ret) krb5_err(context, 1, ret, "hdb_value2entry"); if(print_dump) hdb_print_entry(context, db, &entry, stdout); else { ret = db->hdb_store(context, db, 0, &entry); if(ret == HDB_ERR_EXISTS) { char *s; ret = krb5_unparse_name(context, entry.entry.principal, &s); if (ret) s = strdup(unparseable_name); krb5_warnx(context, "Entry exists: %s", s); free(s); } else if(ret) krb5_err(context, 1, ret, "db_store"); else nprincs++; } hdb_free_entry(context, &entry); } if (!print_dump) krb5_log(context, fac, 0, "Received %d principals", nprincs); if (inetd_flag == 0) rk_closesocket(sock); exit(0); }
krb5_error_code _kdc_pk_rd_padata(krb5_context context, krb5_kdc_configuration *config, const KDC_REQ *req, const PA_DATA *pa, hdb_entry_ex *client, pk_client_params **ret_params) { pk_client_params *cp; krb5_error_code ret; heim_oid eContentType = { 0, NULL }, contentInfoOid = { 0, NULL }; krb5_data eContent = { 0, NULL }; krb5_data signed_content = { 0, NULL }; const char *type = "unknown type"; hx509_certs trust_anchors; int have_data = 0; const HDB_Ext_PKINIT_cert *pc; *ret_params = NULL; if (!config->enable_pkinit) { kdc_log(context, config, 0, "PK-INIT request but PK-INIT not enabled"); krb5_clear_error_message(context); return 0; } cp = calloc(1, sizeof(*cp)); if (cp == NULL) { krb5_clear_error_message(context); ret = ENOMEM; goto out; } ret = hx509_certs_init(context->hx509ctx, "MEMORY:trust-anchors", 0, NULL, &trust_anchors); if (ret) { krb5_set_error_message(context, ret, "failed to create trust anchors"); goto out; } ret = hx509_certs_merge(context->hx509ctx, trust_anchors, kdc_identity->anchors); if (ret) { hx509_certs_free(&trust_anchors); krb5_set_error_message(context, ret, "failed to create verify context"); goto out; } /* Add any registered certificates for this client as trust anchors */ ret = hdb_entry_get_pkinit_cert(&client->entry, &pc); if (ret == 0 && pc != NULL) { hx509_cert cert; unsigned int i; for (i = 0; i < pc->len; i++) { cert = hx509_cert_init_data(context->hx509ctx, pc->val[i].cert.data, pc->val[i].cert.length, NULL); if (cert == NULL) continue; hx509_certs_add(context->hx509ctx, trust_anchors, cert); hx509_cert_free(cert); } } ret = hx509_verify_init_ctx(context->hx509ctx, &cp->verify_ctx); if (ret) { hx509_certs_free(&trust_anchors); krb5_set_error_message(context, ret, "failed to create verify context"); goto out; } hx509_verify_set_time(cp->verify_ctx, kdc_time); hx509_verify_attach_anchors(cp->verify_ctx, trust_anchors); hx509_certs_free(&trust_anchors); if (config->pkinit_allow_proxy_certs) hx509_verify_set_proxy_certificate(cp->verify_ctx, 1); if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) { PA_PK_AS_REQ_Win2k r; type = "PK-INIT-Win2k"; if (_kdc_is_anon_request(&req->req_body)) { ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED; krb5_set_error_message(context, ret, "Anon not supported in RSA mode"); goto out; } ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data, pa->padata_value.length, &r, NULL); if (ret) { krb5_set_error_message(context, ret, "Can't decode " "PK-AS-REQ-Win2k: %d", ret); goto out; } ret = hx509_cms_unwrap_ContentInfo(&r.signed_auth_pack, &contentInfoOid, &signed_content, &have_data); free_PA_PK_AS_REQ_Win2k(&r); if (ret) { krb5_set_error_message(context, ret, "Can't unwrap ContentInfo(win): %d", ret); goto out; } } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) { PA_PK_AS_REQ r; type = "PK-INIT-IETF"; ret = decode_PA_PK_AS_REQ(pa->padata_value.data, pa->padata_value.length, &r, NULL); if (ret) { krb5_set_error_message(context, ret, "Can't decode PK-AS-REQ: %d", ret); goto out; } /* XXX look at r.kdcPkId */ if (r.trustedCertifiers) { ExternalPrincipalIdentifiers *edi = r.trustedCertifiers; unsigned int i, maxedi; ret = hx509_certs_init(context->hx509ctx, "MEMORY:client-anchors", 0, NULL, &cp->client_anchors); if (ret) { krb5_set_error_message(context, ret, "Can't allocate client anchors: %d", ret); goto out; } /* * If the client sent more then 10 EDI, don't bother * looking more then 10 of performance reasons. */ maxedi = edi->len; if (maxedi > 10) maxedi = 10; for (i = 0; i < maxedi; i++) { IssuerAndSerialNumber iasn; hx509_query *q; hx509_cert cert; size_t size; if (edi->val[i].issuerAndSerialNumber == NULL) continue; ret = hx509_query_alloc(context->hx509ctx, &q); if (ret) { krb5_set_error_message(context, ret, "Failed to allocate hx509_query"); goto out; } ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data, edi->val[i].issuerAndSerialNumber->length, &iasn, &size); if (ret) { hx509_query_free(context->hx509ctx, q); continue; } ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber); free_IssuerAndSerialNumber(&iasn); if (ret) { hx509_query_free(context->hx509ctx, q); continue; } ret = hx509_certs_find(context->hx509ctx, kdc_identity->certs, q, &cert); hx509_query_free(context->hx509ctx, q); if (ret) continue; hx509_certs_add(context->hx509ctx, cp->client_anchors, cert); hx509_cert_free(cert); } } ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack, &contentInfoOid, &signed_content, &have_data); free_PA_PK_AS_REQ(&r); if (ret) { krb5_set_error_message(context, ret, "Can't unwrap ContentInfo: %d", ret); goto out; } } else { krb5_clear_error_message(context); ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP; goto out; } ret = der_heim_oid_cmp(&contentInfoOid, &asn1_oid_id_pkcs7_signedData); if (ret != 0) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "PK-AS-REQ-Win2k invalid content type oid"); goto out; } if (!have_data) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "PK-AS-REQ-Win2k no signed auth pack"); goto out; } { hx509_certs signer_certs; int flags = HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH; /* BTMM */ if (_kdc_is_anon_request(&req->req_body)) flags |= HX509_CMS_VS_ALLOW_ZERO_SIGNER; ret = hx509_cms_verify_signed(context->hx509ctx, cp->verify_ctx, flags, signed_content.data, signed_content.length, NULL, kdc_identity->certpool, &eContentType, &eContent, &signer_certs); if (ret) { char *s = hx509_get_error_string(context->hx509ctx, ret); krb5_warnx(context, "PKINIT: failed to verify signature: %s: %d", s, ret); free(s); goto out; } if (signer_certs) { ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &cp->cert); hx509_certs_free(&signer_certs); } if (ret) goto out; } /* Signature is correct, now verify the signed message */ if (der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkcs7_data) != 0 && der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkauthdata) != 0) { ret = KRB5_BADMSGTYPE; krb5_set_error_message(context, ret, "got wrong oid for pkauthdata"); goto out; } if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) { AuthPack_Win2k ap; ret = decode_AuthPack_Win2k(eContent.data, eContent.length, &ap, NULL); if (ret) { krb5_set_error_message(context, ret, "Can't decode AuthPack: %d", ret); goto out; } ret = pk_check_pkauthenticator_win2k(context, &ap.pkAuthenticator, req); if (ret) { free_AuthPack_Win2k(&ap); goto out; } cp->type = PKINIT_WIN2K; cp->nonce = ap.pkAuthenticator.nonce; if (ap.clientPublicValue) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "DH not supported for windows"); goto out; } free_AuthPack_Win2k(&ap); } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) { AuthPack ap; ret = decode_AuthPack(eContent.data, eContent.length, &ap, NULL); if (ret) { krb5_set_error_message(context, ret, "Can't decode AuthPack: %d", ret); free_AuthPack(&ap); goto out; } if (_kdc_is_anon_request(&req->req_body) && ap.clientPublicValue == NULL) { free_AuthPack(&ap); ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED; krb5_set_error_message(context, ret, "Anon not supported in RSA mode"); goto out; } ret = pk_check_pkauthenticator(context, &ap.pkAuthenticator, req); if (ret) { free_AuthPack(&ap); goto out; } cp->type = PKINIT_27; cp->nonce = ap.pkAuthenticator.nonce; if (ap.clientPublicValue) { if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_dhpublicnumber) == 0) { cp->keyex = USE_DH; ret = get_dh_param(context, config, ap.clientPublicValue, cp); } else if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_ecPublicKey) == 0) { cp->keyex = USE_ECDH; ret = _kdc_get_ecdh_param(context, config, ap.clientPublicValue, &cp->u.ecdh.public_key); } else { ret = KRB5_BADMSGTYPE; krb5_set_error_message(context, ret, "PKINIT unknown DH mechanism"); } if (ret) { free_AuthPack(&ap); goto out; } } else cp->keyex = USE_RSA; ret = hx509_peer_info_alloc(context->hx509ctx, &cp->peer); if (ret) { free_AuthPack(&ap); goto out; } if (ap.supportedCMSTypes) { ret = hx509_peer_info_set_cms_algs(context->hx509ctx, cp->peer, ap.supportedCMSTypes->val, ap.supportedCMSTypes->len); if (ret) { free_AuthPack(&ap); goto out; } } else { /* assume old client */ hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer, hx509_crypto_des_rsdi_ede3_cbc()); hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer, hx509_signature_rsa_with_sha1()); hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer, hx509_signature_sha1()); } free_AuthPack(&ap); } else krb5_abortx(context, "internal pkinit error"); kdc_log(context, config, 0, "PK-INIT request of type %s", type); out: if (ret) krb5_warn(context, ret, "PKINIT"); if (signed_content.data) free(signed_content.data); krb5_data_free(&eContent); der_free_oid(&eContentType); der_free_oid(&contentInfoOid); if (ret) { _kdc_pk_free_client_param(context, cp); } else *ret_params = cp; return ret; }
static void receive_loop (krb5_context context, krb5_storage *sp, kadm5_server_context *server_context) { int ret; off_t left, right; void *buf; int32_t vers, vers2; ssize_t sret; /* * Seek to the current version of the local database. */ do { int32_t len, timestamp, tmp; enum kadm_ops op; if(krb5_ret_int32 (sp, &vers) != 0) return; krb5_ret_int32 (sp, ×tamp); krb5_ret_int32 (sp, &tmp); op = tmp; krb5_ret_int32 (sp, &len); if (vers <= server_context->log_context.version) krb5_storage_seek(sp, len + 8, SEEK_CUR); } while(vers <= server_context->log_context.version); /* * Read up rest of the entires into the memory... */ left = krb5_storage_seek (sp, -16, SEEK_CUR); right = krb5_storage_seek (sp, 0, SEEK_END); buf = malloc (right - left); if (buf == NULL && (right - left) != 0) krb5_errx (context, 1, "malloc: no memory"); /* * ...and then write them out to the on-disk log. */ krb5_storage_seek (sp, left, SEEK_SET); krb5_storage_read (sp, buf, right - left); sret = write (server_context->log_context.log_fd, buf, right-left); if (sret != right - left) krb5_err(context, 1, errno, "Failed to write log to disk"); ret = fsync (server_context->log_context.log_fd); if (ret) krb5_err(context, 1, errno, "Failed to sync log to disk"); free (buf); /* * Go back to the startpoint and start to commit the entires to * the database. */ krb5_storage_seek (sp, left, SEEK_SET); for(;;) { int32_t len, len2, timestamp, tmp; off_t cur, cur2; enum kadm_ops op; if(krb5_ret_int32 (sp, &vers) != 0) break; ret = krb5_ret_int32 (sp, ×tamp); if (ret) krb5_errx(context, 1, "entry %ld: too short", (long)vers); ret = krb5_ret_int32 (sp, &tmp); if (ret) krb5_errx(context, 1, "entry %ld: too short", (long)vers); op = tmp; ret = krb5_ret_int32 (sp, &len); if (ret) krb5_errx(context, 1, "entry %ld: too short", (long)vers); if (len < 0) krb5_errx(context, 1, "log is corrupted, " "negative length of entry version %ld: %ld", (long)vers, (long)len); cur = krb5_storage_seek(sp, 0, SEEK_CUR); krb5_warnx (context, "replaying entry %d", (int)vers); ret = kadm5_log_replay (server_context, op, vers, len, sp); if (ret) { const char *s = krb5_get_error_message(server_context->context, ret); krb5_warnx (context, "kadm5_log_replay: %ld. Lost entry entry, " "Database out of sync ?: %s (%d)", (long)vers, s ? s : "unknown error", ret); krb5_free_error_message(context, s); } { /* * Make sure the krb5_log_replay does the right thing wrt * reading out data from the sp. */ cur2 = krb5_storage_seek(sp, 0, SEEK_CUR); if (cur + len != cur2) krb5_errx(context, 1, "kadm5_log_reply version: %ld didn't read the whole entry", (long)vers); } if (krb5_ret_int32 (sp, &len2) != 0) krb5_errx(context, 1, "entry %ld: postamble too short", (long)vers); if(krb5_ret_int32 (sp, &vers2) != 0) krb5_errx(context, 1, "entry %ld: postamble too short", (long)vers); if (len != len2) krb5_errx(context, 1, "entry %ld: len != len2", (long)vers); if (vers != vers2) krb5_errx(context, 1, "entry %ld: vers != vers2", (long)vers); } /* * Update version */ server_context->log_context.version = vers; }
void kadm5_setup_passwd_quality_check(krb5_context context, const char *check_library, const char *check_function) { #ifdef HAVE_DLOPEN void *handle; void *sym; int *version; const char *tmp; if(check_library == NULL) { tmp = krb5_config_get_string(context, NULL, "password_quality", "check_library", NULL); if(tmp != NULL) check_library = tmp; } if(check_function == NULL) { tmp = krb5_config_get_string(context, NULL, "password_quality", "check_function", NULL); if(tmp != NULL) check_function = tmp; } if(check_library != NULL && check_function == NULL) check_function = "passwd_check"; if(check_library == NULL) return; handle = dlopen(check_library, RTLD_NOW); if(handle == NULL) { krb5_warnx(context, "failed to open `%s'", check_library); return; } version = (int *) dlsym(handle, "version"); if(version == NULL) { krb5_warnx(context, "didn't find `version' symbol in `%s'", check_library); dlclose(handle); return; } if(*version != KADM5_PASSWD_VERSION_V0) { krb5_warnx(context, "version of loaded library is %d (expected %d)", *version, KADM5_PASSWD_VERSION_V0); dlclose(handle); return; } sym = dlsym(handle, check_function); if(sym == NULL) { krb5_warnx(context, "didn't find `%s' symbol in `%s'", check_function, check_library); dlclose(handle); return; } passwd_quality_check = (kadm5_passwd_quality_check_func_v0) sym; #endif /* HAVE_DLOPEN */ }
static krb5_error_code process_reply (krb5_context context, krb5_auth_context auth_context, int is_stream, int sock, int *result_code, krb5_data *result_code_string, krb5_data *result_string, const char *host) { krb5_error_code ret; u_char reply[1024 * 3]; ssize_t len; u_int16_t pkt_len, pkt_ver; krb5_data ap_rep_data; int save_errno; len = 0; if (is_stream) { while (len < sizeof(reply)) { unsigned long size; ret = recvfrom (sock, reply + len, sizeof(reply) - len, 0, NULL, NULL); if (ret < 0) { save_errno = errno; krb5_set_error_string(context, "recvfrom %s: %s", host, strerror(save_errno)); return save_errno; } else if (ret == 0) { krb5_set_error_string(context, "recvfrom timeout %s", host); return 1; } len += ret; if (len < 4) continue; _krb5_get_int(reply, &size, 4); if (size + 4 < len) continue; memmove(reply, reply + 4, size); len = size; break; } if (len == sizeof(reply)) { krb5_set_error_string(context, "message too large from %s", host); return ENOMEM; } } else { ret = recvfrom (sock, reply, sizeof(reply), 0, NULL, NULL); if (ret < 0) { save_errno = errno; krb5_set_error_string(context, "recvfrom %s: %s", host, strerror(save_errno)); return save_errno; } len = ret; } if (len < 6) { str2data (result_string, "server %s sent to too short message " "(%ld bytes)", host, (long)len); *result_code = KRB5_KPASSWD_MALFORMED; return 0; } pkt_len = (reply[0] << 8) | (reply[1]); pkt_ver = (reply[2] << 8) | (reply[3]); if ((pkt_len != len) || (reply[1] == 0x7e || reply[1] == 0x5e)) { KRB_ERROR error; size_t size; u_char *p; memset(&error, 0, sizeof(error)); ret = decode_KRB_ERROR(reply, len, &error, &size); if (ret) return ret; if (error.e_data->length < 2) { str2data(result_string, "server %s sent too short " "e_data to print anything usable", host); free_KRB_ERROR(&error); *result_code = KRB5_KPASSWD_MALFORMED; return 0; } p = error.e_data->data; *result_code = (p[0] << 8) | p[1]; if (error.e_data->length == 2) str2data(result_string, "server only sent error code"); else krb5_data_copy (result_string, p + 2, error.e_data->length - 2); free_KRB_ERROR(&error); return 0; } if (pkt_len != len) { str2data (result_string, "client: wrong len in reply"); *result_code = KRB5_KPASSWD_MALFORMED; return 0; } if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW) { str2data (result_string, "client: wrong version number (%d)", pkt_ver); *result_code = KRB5_KPASSWD_MALFORMED; return 0; } ap_rep_data.data = reply + 6; ap_rep_data.length = (reply[4] << 8) | (reply[5]); if (reply + len < (u_char *)ap_rep_data.data + ap_rep_data.length) { str2data (result_string, "client: wrong AP len in reply"); *result_code = KRB5_KPASSWD_MALFORMED; return 0; } if (ap_rep_data.length) { krb5_ap_rep_enc_part *ap_rep; krb5_data priv_data; u_char *p; priv_data.data = (u_char*)ap_rep_data.data + ap_rep_data.length; priv_data.length = len - ap_rep_data.length - 6; ret = krb5_rd_rep (context, auth_context, &ap_rep_data, &ap_rep); if (ret) return ret; krb5_free_ap_rep_enc_part (context, ap_rep); ret = krb5_rd_priv (context, auth_context, &priv_data, result_code_string, NULL); if (ret) { krb5_data_free (result_code_string); return ret; } if (result_code_string->length < 2) { *result_code = KRB5_KPASSWD_MALFORMED; str2data (result_string, "client: bad length in result"); return 0; } p = result_code_string->data; *result_code = (p[0] << 8) | p[1]; krb5_data_copy (result_string, (unsigned char*)result_code_string->data + 2, result_code_string->length - 2); return 0; } else { KRB_ERROR error; size_t size; u_char *p; ret = decode_KRB_ERROR(reply + 6, len - 6, &error, &size); if (ret) { return ret; } if (error.e_data->length < 2) { krb5_warnx (context, "too short e_data to print anything usable"); return 1; /* XXX */ } p = error.e_data->data; *result_code = (p[0] << 8) | p[1]; krb5_data_copy (result_string, p + 2, error.e_data->length - 2); return 0; } }
int main(int argc, char **argv) { krb5_error_code ret; int optidx = 0; int exit_status = 0; setprogname (argv[0]); setlocale (LC_ALL, ""); bindtextdomain ("heimdal_kuser", HEIMDAL_LOCALEDIR); textdomain("heimdal_kuser"); ret = krb5_init_context(&kcc_context); if (ret == KRB5_CONFIG_BADFORMAT) errx (1, "krb5_init_context failed to parse configuration file"); else if (ret) errx(1, "krb5_init_context failed: %d", ret); /* * Support linking of kcc to commands */ if (!command_alias(getprogname())) { if (argc == 1) { sl_slc_help(commands, 0, NULL); return 1; } if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) usage(1); if (help_flag) usage (0); if(version_flag) { print_version(NULL); exit(0); } } else { argv[0] = rk_UNCONST(getprogname()); } argc -= optidx; argv += optidx; if (argc != 0) { ret = sl_command(commands, argc, argv); if(ret == -1) krb5_warnx(kcc_context, "unrecognized command: %s", argv[0]); else if (ret == -2) ret = 0; if(ret != 0) exit_status = 1; } else { sl_slc_help(commands, argc, argv); exit_status = 1; } krb5_free_context(kcc_context); return exit_status; }
static int proto (int sock, const char *hostname, const char *svc, char *message, size_t len) { krb5_auth_context auth_context; krb5_error_code status; krb5_principal server; krb5_data data; krb5_data data_send; krb5_ccache ccache; krb5_creds creds; krb5_kdc_flags flags; krb5_principal principal; status = krb5_auth_con_init (context, &auth_context); if (status) { krb5_warn (context, status, "krb5_auth_con_init"); return 1; } status = krb5_auth_con_setaddrs_from_fd (context, auth_context, &sock); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_auth_con_setaddr"); return 1; } status = krb5_sname_to_principal (context, hostname, svc, KRB5_NT_SRV_HST, &server); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_sname_to_principal"); return 1; } status = krb5_sendauth (context, &auth_context, &sock, KF_VERSION_1, NULL, server, AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, NULL, NULL, NULL, NULL, NULL, NULL); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn(context, status, "krb5_sendauth"); return 1; } if (ccache_name == NULL) ccache_name = ""; data_send.data = (void *)remote_name; data_send.length = strlen(remote_name) + 1; status = krb5_write_priv_message(context, auth_context, &sock, &data_send); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_write_message"); return 1; } data_send.data = (void *)ccache_name; data_send.length = strlen(ccache_name)+1; status = krb5_write_priv_message(context, auth_context, &sock, &data_send); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_write_message"); return 1; } memset (&creds, 0, sizeof(creds)); status = krb5_cc_default (context, &ccache); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_cc_default"); return 1; } status = krb5_cc_get_principal (context, ccache, &principal); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_cc_get_principal"); return 1; } creds.client = principal; status = krb5_make_principal (context, &creds.server, principal->realm, KRB5_TGS_NAME, principal->realm, NULL); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_make_principal"); return 1; } creds.times.endtime = 0; flags.i = 0; flags.b.forwarded = 1; flags.b.forwardable = forwardable; status = krb5_get_forwarded_creds (context, auth_context, ccache, flags.i, hostname, &creds, &data); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_get_forwarded_creds"); return 1; } status = krb5_write_priv_message(context, auth_context, &sock, &data); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_mk_priv"); return 1; } krb5_data_free (&data); status = krb5_read_priv_message(context, auth_context, &sock, &data); krb5_auth_con_free(context, auth_context); if (status) { krb5_warn (context, status, "krb5_mk_priv"); return 1; } if(data.length >= len) { krb5_warnx (context, "returned string is too long, truncating"); memcpy(message, data.data, len); message[len - 1] = '\0'; } else { memcpy(message, data.data, data.length); message[data.length] = '\0'; } krb5_data_free (&data); return(strcmp(message, "ok")); }
static int process_msg (kadm5_server_context *server_context, slave *s, int log_fd, const char *database, uint32_t current_version, uint32_t current_tstamp) { krb5_context context = server_context->context; int ret = 0; krb5_data out; krb5_storage *sp; uint32_t tmp; ret = krb5_read_priv_message(context, s->ac, &s->fd, &out); if(ret) { krb5_warn(context, ret, "error reading message from %s", s->name); return 1; } sp = krb5_storage_from_mem(out.data, out.length); if (sp == NULL) { krb5_warnx(context, "process_msg: no memory"); krb5_data_free(&out); return 1; } if (krb5_ret_uint32(sp, &tmp) != 0) { krb5_warnx(context, "process_msg: client send too short command"); krb5_data_free(&out); return 1; } switch (tmp) { case I_HAVE : ret = krb5_ret_uint32(sp, &tmp); if (ret != 0) { krb5_warnx(context, "process_msg: client send too little I_HAVE data"); break; } /* new started slave that have old log */ if (s->version == 0 && tmp != 0) { if (current_version < tmp) { krb5_warnx(context, "Slave %s (version %u) have later version " "the master (version %u) OUT OF SYNC", s->name, tmp, current_version); } if (verbose) krb5_warnx(context, "slave %s updated from %u to %u", s->name, s->version, tmp); s->version = tmp; } if (tmp < s->version) { krb5_warnx(context, "Slave %s claims to not have " "version we already sent to it", s->name); s->version = tmp; } ret = send_diffs(server_context, s, log_fd, database, current_version, current_tstamp); break; case I_AM_HERE : if (verbose) krb5_warnx(context, "slave %s is there", s->name); break; case ARE_YOU_THERE: case FOR_YOU : default : krb5_warnx(context, "Ignoring command %d", tmp); break; } krb5_data_free(&out); krb5_storage_free(sp); slave_seen(s); return ret; }
krb5_error_code krb5_kdc_pk_initialize(krb5_context context, krb5_kdc_configuration *config, const char *user_id, const char *anchors, char **pool, char **revoke_list) { const char *file; char *fn = NULL; krb5_error_code ret; file = krb5_config_get_string(context, NULL, "libdefaults", "moduli", NULL); ret = _krb5_parse_moduli(context, file, &moduli); if (ret) krb5_err(context, 1, ret, "PKINIT: failed to load modidi file"); principal_mappings.len = 0; principal_mappings.val = NULL; ret = _krb5_pk_load_id(context, &kdc_identity, user_id, anchors, pool, revoke_list, NULL, NULL, NULL); if (ret) { krb5_warn(context, ret, "PKINIT: "); config->enable_pkinit = 0; return ret; } { hx509_query *q; hx509_cert cert; ret = hx509_query_alloc(context->hx509ctx, &q); if (ret) { krb5_warnx(context, "PKINIT: out of memory"); return ENOMEM; } hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); if (config->pkinit_kdc_friendly_name) hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name); ret = hx509_certs_find(context->hx509ctx, kdc_identity->certs, q, &cert); hx509_query_free(context->hx509ctx, q); if (ret == 0) { if (hx509_cert_check_eku(context->hx509ctx, cert, &asn1_oid_id_pkkdcekuoid, 0)) { hx509_name name; char *str; ret = hx509_cert_get_subject(cert, &name); if (ret == 0) { hx509_name_to_string(name, &str); krb5_warnx(context, "WARNING Found KDC certificate (%s)" "is missing the PK-INIT KDC EKU, this is bad for " "interoperability.", str); hx509_name_free(&name); free(str); } } hx509_cert_free(cert); } else krb5_warnx(context, "PKINIT: failed to find a signing " "certifiate with a public key"); } if (krb5_config_get_bool_default(context, NULL, FALSE, "kdc", "pkinit_allow_proxy_certificate", NULL)) config->pkinit_allow_proxy_certs = 1; file = krb5_config_get_string(context, NULL, "kdc", "pkinit_mappings_file", NULL); if (file == NULL) { int aret; aret = asprintf(&fn, "%s/pki-mapping", hdb_db_dir(context)); if (aret == -1) { krb5_warnx(context, "PKINIT: out of memory"); return ENOMEM; } file = fn; } load_mappings(context, file); if (fn) free(fn); return 0; }
static krb5_error_code receive_everything (krb5_context context, int fd, kadm5_server_context *server_context, krb5_auth_context auth_context) { int ret; krb5_data data; int32_t vno = 0; int32_t opcode; krb5_storage *sp; char *dbname; HDB *mydb; krb5_warnx(context, "receive complete database"); asprintf(&dbname, "%s-NEW", server_context->db->hdb_name); ret = hdb_create(context, &mydb, dbname); if(ret) krb5_err(context,1, ret, "hdb_create"); free(dbname); ret = hdb_set_master_keyfile (context, mydb, server_context->config.stash_file); if(ret) krb5_err(context,1, ret, "hdb_set_master_keyfile"); /* I really want to use O_EXCL here, but given that I can't easily clean up on error, I won't */ ret = mydb->hdb_open(context, mydb, O_RDWR | O_CREAT | O_TRUNC, 0600); if (ret) krb5_err (context, 1, ret, "db->open"); sp = NULL; do { ret = krb5_read_priv_message(context, auth_context, &fd, &data); if (ret) { krb5_warn (context, ret, "krb5_read_priv_message"); goto cleanup; } sp = krb5_storage_from_data (&data); if (sp == NULL) krb5_errx (context, 1, "krb5_storage_from_data"); krb5_ret_int32 (sp, &opcode); if (opcode == ONE_PRINC) { krb5_data fake_data; hdb_entry_ex entry; krb5_storage_free(sp); fake_data.data = (char *)data.data + 4; fake_data.length = data.length - 4; memset(&entry, 0, sizeof(entry)); ret = hdb_value2entry (context, &fake_data, &entry.entry); if (ret) krb5_err (context, 1, ret, "hdb_value2entry"); ret = mydb->hdb_store(server_context->context, mydb, 0, &entry); if (ret) krb5_err (context, 1, ret, "hdb_store"); hdb_free_entry (context, &entry); krb5_data_free (&data); } else if (opcode == NOW_YOU_HAVE) ; else krb5_errx (context, 1, "strange opcode %d", opcode); } while (opcode == ONE_PRINC); if (opcode != NOW_YOU_HAVE) krb5_errx (context, 1, "receive_everything: strange %d", opcode); krb5_ret_int32 (sp, &vno); krb5_storage_free(sp); ret = kadm5_log_reinit (server_context); if (ret) krb5_err(context, 1, ret, "kadm5_log_reinit"); ret = kadm5_log_set_version (server_context, vno - 1); if (ret) krb5_err (context, 1, ret, "kadm5_log_set_version"); ret = kadm5_log_nop (server_context); if (ret) krb5_err (context, 1, ret, "kadm5_log_nop"); ret = mydb->hdb_rename (context, mydb, server_context->db->hdb_name); if (ret) krb5_err (context, 1, ret, "db->rename"); cleanup: krb5_data_free (&data); ret = mydb->hdb_close (context, mydb); if (ret) krb5_err (context, 1, ret, "db->close"); ret = mydb->hdb_destroy (context, mydb); if (ret) krb5_err (context, 1, ret, "db->destroy"); krb5_warnx(context, "receive complete database, version %ld", (long)vno); return ret; }
static kadm5_ret_t kadm_connect(kadm5_client_context *ctx) { kadm5_ret_t ret; krb5_principal server; krb5_ccache cc; rk_socket_t s = rk_INVALID_SOCKET; struct addrinfo *ai, *a; struct addrinfo hints; int error; char portstr[NI_MAXSERV]; char *hostname, *slash; char *service_name; krb5_context context = ctx->context; memset (&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; snprintf (portstr, sizeof(portstr), "%u", ntohs(ctx->kadmind_port)); hostname = ctx->admin_server; slash = strchr (hostname, '/'); if (slash != NULL) hostname = slash + 1; error = getaddrinfo (hostname, portstr, &hints, &ai); if (error) { krb5_clear_error_message(context); return KADM5_BAD_SERVER_NAME; } for (a = ai; a != NULL; a = a->ai_next) { s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); if (s < 0) continue; if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { krb5_clear_error_message(context); krb5_warn (context, errno, "connect(%s)", hostname); rk_closesocket (s); continue; } break; } if (a == NULL) { freeaddrinfo (ai); krb5_clear_error_message(context); krb5_warnx (context, "failed to contact %s", hostname); return KADM5_FAILURE; } ret = _kadm5_c_get_cred_cache(context, ctx->client_name, ctx->service_name, NULL, ctx->prompter, ctx->keytab, ctx->ccache, &cc); if(ret) { freeaddrinfo (ai); rk_closesocket(s); return ret; } if (ctx->realm) asprintf(&service_name, "%s@%s", KADM5_ADMIN_SERVICE, ctx->realm); else asprintf(&service_name, "%s", KADM5_ADMIN_SERVICE); if (service_name == NULL) { freeaddrinfo (ai); rk_closesocket(s); krb5_clear_error_message(context); return ENOMEM; } ret = krb5_parse_name(context, service_name, &server); free(service_name); if(ret) { freeaddrinfo (ai); if(ctx->ccache == NULL) krb5_cc_close(context, cc); rk_closesocket(s); return ret; } ctx->ac = NULL; ret = krb5_sendauth(context, &ctx->ac, &s, KADMIN_APPL_VERSION, NULL, server, AP_OPTS_MUTUAL_REQUIRED, NULL, NULL, cc, NULL, NULL, NULL); if(ret == 0) { krb5_data params; kadm5_config_params p; memset(&p, 0, sizeof(p)); if(ctx->realm) { p.mask |= KADM5_CONFIG_REALM; p.realm = ctx->realm; } ret = _kadm5_marshal_params(context, &p, ¶ms); ret = krb5_write_priv_message(context, ctx->ac, &s, ¶ms); krb5_data_free(¶ms); if(ret) { freeaddrinfo (ai); rk_closesocket(s); if(ctx->ccache == NULL) krb5_cc_close(context, cc); return ret; } } else if(ret == KRB5_SENDAUTH_BADAPPLVERS) { rk_closesocket(s); s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); if (s < 0) { freeaddrinfo (ai); krb5_clear_error_message(context); return errno; } if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { rk_closesocket (s); freeaddrinfo (ai); krb5_clear_error_message(context); return errno; } ret = krb5_sendauth(context, &ctx->ac, &s, KADMIN_OLD_APPL_VERSION, NULL, server, AP_OPTS_MUTUAL_REQUIRED, NULL, NULL, cc, NULL, NULL, NULL); } freeaddrinfo (ai); if(ret) { rk_closesocket(s); return ret; } krb5_free_principal(context, server); if(ctx->ccache == NULL) krb5_cc_close(context, cc); ctx->sock = s; 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 void add_slave (krb5_context context, krb5_keytab keytab, slave **root, int fd) { krb5_principal server; krb5_error_code ret; slave *s; socklen_t addr_len; krb5_ticket *ticket = NULL; char hostname[128]; s = malloc(sizeof(*s)); if (s == NULL) { krb5_warnx (context, "add_slave: no memory"); return; } s->name = NULL; s->ac = NULL; addr_len = sizeof(s->addr); s->fd = accept (fd, (struct sockaddr *)&s->addr, &addr_len); if (s->fd < 0) { krb5_warn (context, errno, "accept"); goto error; } gethostname(hostname, sizeof(hostname)); ret = krb5_sname_to_principal (context, hostname, IPROP_NAME, KRB5_NT_SRV_HST, &server); if (ret) { krb5_warn (context, ret, "krb5_sname_to_principal"); goto error; } ret = krb5_recvauth (context, &s->ac, &s->fd, IPROP_VERSION, server, 0, keytab, &ticket); krb5_free_principal (context, server); if (ret) { krb5_warn (context, ret, "krb5_recvauth"); goto error; } ret = krb5_unparse_name (context, ticket->client, &s->name); if (ret) { krb5_warn (context, ret, "krb5_unparse_name"); goto error; } if (check_acl (context, s->name)) { krb5_warnx (context, "%s not in acl", s->name); goto error; } krb5_free_ticket (context, ticket); ticket = NULL; { slave *l = *root; while (l) { if (strcmp(l->name, s->name) == 0) break; l = l->next; } if (l) { if (l->flags & SLAVE_F_DEAD) { remove_slave(context, l, root); } else { krb5_warnx (context, "second connection from %s", s->name); goto error; } } } krb5_warnx (context, "connection from %s", s->name); s->version = 0; s->flags = 0; slave_seen(s); s->next = *root; *root = s; return; error: remove_slave(context, s, root); }
int kt_get(struct get_options *opt, int argc, char **argv) { krb5_error_code ret = 0; krb5_keytab keytab; void *kadm_handle = NULL; krb5_enctype *etypes = NULL; size_t netypes = 0; size_t i; int a, j; unsigned int failed = 0; if((keytab = ktutil_open_keytab()) == NULL) return 1; if(opt->realm_string) krb5_set_default_realm(context, opt->realm_string); if (opt->enctypes_strings.num_strings != 0) { etypes = malloc (opt->enctypes_strings.num_strings * sizeof(*etypes)); if (etypes == NULL) { krb5_warnx(context, "malloc failed"); goto out; } netypes = opt->enctypes_strings.num_strings; for(i = 0; i < netypes; i++) { ret = krb5_string_to_enctype(context, opt->enctypes_strings.strings[i], &etypes[i]); if(ret) { krb5_warnx(context, "unrecognized enctype: %s", opt->enctypes_strings.strings[i]); goto out; } } } for(a = 0; a < argc; a++){ krb5_principal princ_ent; kadm5_principal_ent_rec princ; int mask = 0; krb5_keyblock *keys; int n_keys; int created = 0; krb5_keytab_entry entry; ret = krb5_parse_name(context, argv[a], &princ_ent); if (ret) { krb5_warn(context, ret, "can't parse principal %s", argv[a]); failed++; continue; } memset(&princ, 0, sizeof(princ)); princ.principal = princ_ent; mask |= KADM5_PRINCIPAL; princ.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; mask |= KADM5_ATTRIBUTES; princ.princ_expire_time = 0; mask |= KADM5_PRINC_EXPIRE_TIME; if(kadm_handle == NULL) { const char *r; if(opt->realm_string != NULL) r = opt->realm_string; else r = krb5_principal_get_realm(context, princ_ent); kadm_handle = open_kadmin_connection(opt->principal_string, r, opt->admin_server_string, opt->server_port_integer); if(kadm_handle == NULL) break; } ret = kadm5_create_principal(kadm_handle, &princ, mask, "x"); if(ret == 0) created = 1; else if(ret != KADM5_DUP) { krb5_warn(context, ret, "kadm5_create_principal(%s)", argv[a]); krb5_free_principal(context, princ_ent); failed++; continue; } ret = kadm5_randkey_principal(kadm_handle, princ_ent, &keys, &n_keys); if (ret) { krb5_warn(context, ret, "kadm5_randkey_principal(%s)", argv[a]); krb5_free_principal(context, princ_ent); failed++; continue; } ret = kadm5_get_principal(kadm_handle, princ_ent, &princ, KADM5_PRINCIPAL | KADM5_KVNO | KADM5_ATTRIBUTES); if (ret) { krb5_warn(context, ret, "kadm5_get_principal(%s)", argv[a]); for (j = 0; j < n_keys; j++) krb5_free_keyblock_contents(context, &keys[j]); krb5_free_principal(context, princ_ent); failed++; continue; } if(!created && (princ.attributes & KRB5_KDB_DISALLOW_ALL_TIX)) krb5_warnx(context, "%s: disallow-all-tix flag set - clearing", argv[a]); princ.attributes &= (~KRB5_KDB_DISALLOW_ALL_TIX); mask = KADM5_ATTRIBUTES; if(created) { princ.kvno = 1; mask |= KADM5_KVNO; } ret = kadm5_modify_principal(kadm_handle, &princ, mask); if (ret) { krb5_warn(context, ret, "kadm5_modify_principal(%s)", argv[a]); for (j = 0; j < n_keys; j++) krb5_free_keyblock_contents(context, &keys[j]); krb5_free_principal(context, princ_ent); failed++; continue; } for(j = 0; j < n_keys; j++) { int do_add = TRUE; if (netypes) { size_t k; do_add = FALSE; for (k = 0; k < netypes; ++k) if (keys[j].keytype == etypes[k]) { do_add = TRUE; break; } } if (do_add) { entry.principal = princ_ent; entry.vno = princ.kvno; entry.keyblock = keys[j]; entry.timestamp = time (NULL); ret = krb5_kt_add_entry(context, keytab, &entry); if (ret) krb5_warn(context, ret, "krb5_kt_add_entry"); } krb5_free_keyblock_contents(context, &keys[j]); } kadm5_free_principal_ent(kadm_handle, &princ); krb5_free_principal(context, princ_ent); } out: free(etypes); if (kadm_handle) kadm5_destroy(kadm_handle); krb5_kt_close(context, keytab); return ret != 0 || failed > 0; }
static int string_to_key_test(krb5_context context) { krb5_data password, opaque; krb5_error_code ret; krb5_salt salt; int val = 0; char iter[4]; size_t i; for (i = 0; i < sizeof(keys)/sizeof(keys[0]); i++) { password.data = keys[i].password; password.length = strlen(password.data); salt.salttype = KRB5_PW_SALT; salt.saltvalue.data = keys[i].salt; if (keys[i].saltlen == -1) salt.saltvalue.length = strlen(salt.saltvalue.data); else salt.saltvalue.length = keys[i].saltlen; opaque.data = iter; opaque.length = sizeof(iter); _krb5_put_int(iter, keys[i].iterations, 4); if (keys[i].pbkdf2) { unsigned char keyout[32]; if (keys[i].keylen > sizeof(keyout)) abort(); PKCS5_PBKDF2_HMAC_SHA1(password.data, password.length, salt.saltvalue.data, salt.saltvalue.length, keys[i].iterations, keys[i].keylen, keyout); if (memcmp(keyout, keys[i].pbkdf2, keys[i].keylen) != 0) { krb5_warnx(context, "%d: pbkdf2", (int)i); val = 1; continue; } if (verbose) { printf("PBKDF2:\n"); hex_dump_data(keyout, keys[i].keylen); } } { krb5_keyblock key; ret = krb5_string_to_key_data_salt_opaque (context, keys[i].enctype, password, salt, opaque, &key); if (ret) { krb5_warn(context, ret, "%d: string_to_key_data_salt_opaque", (int)i); val = 1; continue; } if (key.keyvalue.length != keys[i].keylen) { krb5_warnx(context, "%d: key wrong length (%lu/%lu)", (int)i, (unsigned long)key.keyvalue.length, (unsigned long)keys[i].keylen); val = 1; continue; } if (memcmp(key.keyvalue.data, keys[i].key, keys[i].keylen) != 0) { krb5_warnx(context, "%d: key wrong", (int)i); val = 1; continue; } if (verbose) { printf("key:\n"); hex_dump_data(key.keyvalue.data, key.keyvalue.length); } krb5_free_keyblock_contents(context, &key); } } return val; }
int main(int argc, char **argv) { krb5_error_code ret; krb5_context context; void *kadm_handle; kadm5_server_context *server_context; kadm5_config_params conf; krb5_socket_t signal_fd, listen_fd; int log_fd; slave *slaves = NULL; uint32_t current_version = 0, old_version = 0; uint32_t current_tstamp = 0; krb5_keytab keytab; char **files; int aret; int optidx = 0; int restarter_fd = -1; struct stat st; setprogname(argv[0]); if (getarg(args, num_args, argc, argv, &optidx)) krb5_std_usage(1, args, num_args); if (help_flag) krb5_std_usage(0, args, num_args); 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) { aret = asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context)); if (aret == -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"); time_before_gone = parse_time (slave_time_gone, "s"); if (time_before_gone < 0) krb5_errx (context, 1, "couldn't parse time: %s", slave_time_gone); time_before_missing = parse_time (slave_time_missing, "s"); if (time_before_missing < 0) krb5_errx (context, 1, "couldn't parse time: %s", slave_time_missing); krb5_openlog(context, "ipropd-master", &log_facility); krb5_set_warn_dest(context, log_facility); ret = krb5_kt_register(context, &hdb_get_kt_ops); if(ret) krb5_err(context, 1, ret, "krb5_kt_register"); ret = krb5_kt_resolve(context, keytab_str, &keytab); if(ret) krb5_err(context, 1, ret, "krb5_kt_resolve: %s", keytab_str); memset(&conf, 0, sizeof(conf)); if(realm) { conf.mask |= KADM5_CONFIG_REALM; conf.realm = realm; } ret = kadm5_init_with_skey_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; log_fd = open (server_context->log_context.log_file, O_RDONLY, 0); if (log_fd < 0) krb5_err (context, 1, errno, "open %s", server_context->log_context.log_file); if (fstat(log_fd, &st) == -1) krb5_err(context, 1, errno, "stat %s", server_context->log_context.log_file); if (flock(log_fd, LOCK_SH) == -1) krb5_err(context, 1, errno, "shared flock %s", server_context->log_context.log_file); kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_LAST, ¤t_version, ¤t_tstamp); flock(log_fd, LOCK_UN); signal_fd = make_signal_socket (context); listen_fd = make_listen_socket (context, port_str); krb5_warnx(context, "ipropd-master started at version: %lu", (unsigned long)current_version); roken_detach_finish(NULL, daemon_child); restarter_fd = restarter(context, NULL); while (exit_flag == 0){ slave *p; fd_set readset; int max_fd = 0; struct timeval to = {30, 0}; uint32_t vers; struct stat st2;; #ifndef NO_LIMIT_FD_SETSIZE if (signal_fd >= FD_SETSIZE || listen_fd >= FD_SETSIZE || restarter_fd >= FD_SETSIZE) krb5_errx (context, IPROPD_RESTART, "fd too large"); #endif FD_ZERO(&readset); FD_SET(signal_fd, &readset); max_fd = max(max_fd, signal_fd); FD_SET(listen_fd, &readset); max_fd = max(max_fd, listen_fd); if (restarter_fd > -1) { FD_SET(restarter_fd, &readset); max_fd = max(max_fd, restarter_fd); } for (p = slaves; p != NULL; p = p->next) { if (p->flags & SLAVE_F_DEAD) continue; FD_SET(p->fd, &readset); max_fd = max(max_fd, p->fd); } ret = select (max_fd + 1, &readset, NULL, NULL, &to); if (ret < 0) { if (errno == EINTR) continue; else krb5_err (context, IPROPD_RESTART, errno, "select"); } if (stat(server_context->log_context.log_file, &st2) == -1) { krb5_warn(context, errno, "could not stat log file by path"); st2 = st; } if (st2.st_dev != st.st_dev || st2.st_ino != st.st_ino) { (void) close(log_fd); log_fd = open(server_context->log_context.log_file, O_RDONLY, 0); if (log_fd < 0) krb5_err(context, 1, IPROPD_RESTART_SLOW, "open %s", server_context->log_context.log_file); if (fstat(log_fd, &st) == -1) krb5_err(context, IPROPD_RESTART_SLOW, errno, "stat %s", server_context->log_context.log_file); if (flock(log_fd, LOCK_SH) == -1) krb5_err(context, IPROPD_RESTART, errno, "shared flock %s", server_context->log_context.log_file); kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_LAST, ¤t_version, ¤t_tstamp); flock(log_fd, LOCK_UN); } if (ret == 0) { /* Recover from failed transactions */ if (kadm5_log_init_nb(server_context) == 0) kadm5_log_end(server_context); if (flock(log_fd, LOCK_SH) == -1) krb5_err(context, IPROPD_RESTART, errno, "could not lock log file"); kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_LAST, ¤t_version, ¤t_tstamp); flock(log_fd, LOCK_UN); if (current_version > old_version) { krb5_warnx(context, "Missed a signal, updating slaves %lu to %lu", (unsigned long)old_version, (unsigned long)current_version); for (p = slaves; p != NULL; p = p->next) { if (p->flags & SLAVE_F_DEAD) continue; send_diffs (server_context, p, log_fd, database, current_version, current_tstamp); } old_version = current_version; } } if (ret && FD_ISSET(restarter_fd, &readset)) { exit_flag = SIGTERM; break; } if (ret && FD_ISSET(signal_fd, &readset)) { #ifndef NO_UNIX_SOCKETS struct sockaddr_un peer_addr; #else struct sockaddr_storage peer_addr; #endif socklen_t peer_len = sizeof(peer_addr); if(recvfrom(signal_fd, (void *)&vers, sizeof(vers), 0, (struct sockaddr *)&peer_addr, &peer_len) < 0) { krb5_warn (context, errno, "recvfrom"); continue; } --ret; assert(ret >= 0); old_version = current_version; if (flock(log_fd, LOCK_SH) == -1) krb5_err(context, IPROPD_RESTART, errno, "shared flock %s", server_context->log_context.log_file); kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_LAST, ¤t_version, ¤t_tstamp); flock(log_fd, LOCK_UN); if (current_version != old_version) { /* * If current_version < old_version then the log got * truncated and we'll end up doing full propagations. * * Truncating the log when the current version is * numerically small can lead to race conditions. * Ideally we should identify log versions as * {init_or_trunc_time, vno}, then we could not have any * such race conditions, but this would either require * breaking backwards compatibility for the protocol or * adding new messages to it. */ krb5_warnx(context, "Got a signal, updating slaves %lu to %lu", (unsigned long)old_version, (unsigned long)current_version); for (p = slaves; p != NULL; p = p->next) { if (p->flags & SLAVE_F_DEAD) continue; send_diffs (server_context, p, log_fd, database, current_version, current_tstamp); } } else { krb5_warnx(context, "Got a signal, but no update in log version %lu", (unsigned long)current_version); } } for(p = slaves; p != NULL; p = p->next) { if (p->flags & SLAVE_F_DEAD) continue; if (ret && FD_ISSET(p->fd, &readset)) { --ret; assert(ret >= 0); if(process_msg (server_context, p, log_fd, database, current_version, current_tstamp)) slave_dead(context, p); } else if (slave_gone_p (p)) slave_dead(context, p); else if (slave_missing_p (p)) send_are_you_there (context, p); } if (ret && FD_ISSET(listen_fd, &readset)) { add_slave (context, keytab, &slaves, listen_fd); --ret; assert(ret >= 0); } write_stats(context, slaves, current_version); } if(exit_flag == SIGINT || exit_flag == SIGTERM) krb5_warnx(context, "%s terminated", getprogname()); #ifdef SIGXCPU else if(exit_flag == SIGXCPU) krb5_warnx(context, "%s CPU time limit exceeded", getprogname()); #endif else krb5_warnx(context, "%s unexpected exit reason: %ld", getprogname(), (long)exit_flag); write_master_down(context); return 0; }
int init(struct init_options *opt, int argc, char **argv) { kadm5_ret_t ret; int i; HDB *db; krb5_deltat max_life = 0, max_rlife = 0; if (!local_flag) { krb5_warnx(context, "init is only available in local (-l) mode"); return 0; } if (opt->realm_max_ticket_life_string) { if (str2deltat (opt->realm_max_ticket_life_string, &max_life) != 0) { krb5_warnx (context, "unable to parse \"%s\"", opt->realm_max_ticket_life_string); return 0; } } if (opt->realm_max_renewable_life_string) { if (str2deltat (opt->realm_max_renewable_life_string, &max_rlife) != 0) { krb5_warnx (context, "unable to parse \"%s\"", opt->realm_max_renewable_life_string); return 0; } } db = _kadm5_s_get_db(kadm_handle); ret = db->hdb_open(context, db, O_RDWR | O_CREAT, 0600); if(ret){ krb5_warn(context, ret, "hdb_open"); return 0; } db->hdb_close(context, db); for(i = 0; i < argc; i++){ krb5_principal princ; const char *realm = argv[i]; if (opt->realm_max_ticket_life_string == NULL) { max_life = 0; if(edit_deltat ("Realm max ticket life", &max_life, NULL, 0)) { return 0; } } if (opt->realm_max_renewable_life_string == NULL) { max_rlife = 0; if(edit_deltat("Realm max renewable ticket life", &max_rlife, NULL, 0)) { return 0; } } /* Create `krbtgt/REALM' */ ret = krb5_make_principal(context, &princ, realm, KRB5_TGS_NAME, realm, NULL); if(ret) return 0; create_random_entry(princ, max_life, max_rlife, 0); krb5_free_principal(context, princ); if (opt->bare_flag) continue; /* Create `kadmin/changepw' */ krb5_make_principal(context, &princ, realm, "kadmin", "changepw", NULL); /* * The Windows XP (at least) password changing protocol * request the `kadmin/changepw' ticket with `renewable_ok, * renewable, forwardable' and so fails if we disallow * forwardable here. */ create_random_entry(princ, 5*60, 5*60, KRB5_KDB_DISALLOW_TGT_BASED| KRB5_KDB_PWCHANGE_SERVICE| KRB5_KDB_DISALLOW_POSTDATED| KRB5_KDB_DISALLOW_RENEWABLE| KRB5_KDB_DISALLOW_PROXIABLE| KRB5_KDB_REQUIRES_PRE_AUTH); krb5_free_principal(context, princ); /* Create `kadmin/admin' */ krb5_make_principal(context, &princ, realm, "kadmin", "admin", NULL); create_random_entry(princ, 60*60, 60*60, KRB5_KDB_REQUIRES_PRE_AUTH); krb5_free_principal(context, princ); /* Create `changepw/kerberos' (for v4 compat) */ krb5_make_principal(context, &princ, realm, "changepw", "kerberos", NULL); create_random_entry(princ, 60*60, 60*60, KRB5_KDB_DISALLOW_TGT_BASED| KRB5_KDB_PWCHANGE_SERVICE); krb5_free_principal(context, princ); /* Create `kadmin/hprop' for database propagation */ krb5_make_principal(context, &princ, realm, "kadmin", "hprop", NULL); create_random_entry(princ, 60*60, 60*60, KRB5_KDB_REQUIRES_PRE_AUTH| KRB5_KDB_DISALLOW_TGT_BASED); krb5_free_principal(context, princ); /* Create `WELLKNOWN/ANONYMOUS' for anonymous as-req */ krb5_make_principal(context, &princ, realm, KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, NULL); create_random_entry(princ, 60*60, 60*60, KRB5_KDB_REQUIRES_PRE_AUTH); krb5_free_principal(context, princ); /* Create `default' */ { kadm5_principal_ent_rec ent; int mask = 0; memset (&ent, 0, sizeof(ent)); mask |= KADM5_PRINCIPAL; krb5_make_principal(context, &ent.principal, realm, "default", NULL); mask |= KADM5_MAX_LIFE; ent.max_life = 24 * 60 * 60; mask |= KADM5_MAX_RLIFE; ent.max_renewable_life = 7 * ent.max_life; ent.attributes = KRB5_KDB_DISALLOW_ALL_TIX; mask |= KADM5_ATTRIBUTES; ret = kadm5_create_principal(kadm_handle, &ent, mask, ""); if (ret) krb5_err (context, 1, ret, "kadm5_create_principal"); krb5_free_principal(context, ent.principal); } } return 0; }
static int write_dump (krb5_context context, krb5_storage *dump, const char *database, uint32_t current_version) { krb5_error_code ret; krb5_storage *sp; HDB *db; krb5_data data; char buf[8]; /* we assume that the caller has obtained an exclusive lock */ ret = krb5_storage_truncate(dump, 0); if (ret) return ret; if (krb5_storage_seek(dump, 0, SEEK_SET) != 0) return errno; /* * First we store zero as the HDB version, this will indicate to a * later reader that the dumpfile is invalid. We later write the * correct version in the file after we have written all of the * messages. A dump with a zero version will not be considered * to be valid. */ ret = krb5_store_uint32(dump, 0); ret = hdb_create (context, &db, database); if (ret) krb5_err (context, IPROPD_RESTART, ret, "hdb_create: %s", database); ret = db->hdb_open (context, db, O_RDONLY, 0); if (ret) krb5_err (context, IPROPD_RESTART, ret, "db->open"); sp = krb5_storage_from_mem (buf, 4); if (sp == NULL) krb5_errx (context, IPROPD_RESTART, "krb5_storage_from_mem"); krb5_store_uint32 (sp, TELL_YOU_EVERYTHING); krb5_storage_free (sp); data.data = buf; data.length = 4; ret = krb5_store_data(dump, data); if (ret) { krb5_warn (context, ret, "write_dump"); return ret; } ret = hdb_foreach (context, db, HDB_F_ADMIN_DATA, dump_one, dump); if (ret) { krb5_warn (context, ret, "write_dump: hdb_foreach"); return ret; } (*db->hdb_close)(context, db); (*db->hdb_destroy)(context, db); sp = krb5_storage_from_mem (buf, 8); if (sp == NULL) krb5_errx (context, IPROPD_RESTART, "krb5_storage_from_mem"); ret = krb5_store_uint32(sp, NOW_YOU_HAVE); if (ret == 0) krb5_store_uint32(sp, current_version); krb5_storage_free (sp); data.length = 8; if (ret == 0) ret = krb5_store_data(dump, data); /* * We must ensure that the entire valid dump is written to disk * before we write the current version at the front thus making * it a valid dump file. If we crash around here, this can be * important upon reboot. */ if (ret == 0) ret = krb5_storage_fsync(dump); if (ret == 0 && krb5_storage_seek(dump, 0, SEEK_SET) == -1) ret = errno; /* Write current version at the front making the dump valid */ if (ret == 0) ret = krb5_store_uint32(dump, current_version); /* * We don't need to fsync(2) after the real version is written as * it is not a disaster if it doesn't make it to disk if we crash. * After all, we'll just create a new dumpfile. */ if (ret == 0) krb5_warnx(context, "wrote new dumpfile (version %u)", current_version); else krb5_warn(context, ret, "failed to write new dumpfile (version %u)", current_version); return ret; }
int main(int argc, char **argv) { krb5_error_code ret; krb5_context context; krb5_ccache ccache; krb5_principal principal = NULL; int optidx = 0; krb5_deltat ticket_life = 0; #ifdef HAVE_SIGACTION struct sigaction sa; #endif setprogname(argv[0]); setlocale(LC_ALL, ""); bindtextdomain("heimdal_kuser", HEIMDAL_LOCALEDIR); textdomain("heimdal_kuser"); ret = krb5_init_context(&context); if (ret == KRB5_CONFIG_BADFORMAT) errx(1, "krb5_init_context failed to parse configuration file"); else if (ret) errx(1, "krb5_init_context failed: %d", ret); if (getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) usage(1); if (help_flag) usage(0); if (version_flag) { print_version(NULL); exit(0); } argc -= optidx; argv += optidx; /* * Open the keytab now, we use the keytab to determine the principal's * realm when the requested principal has no realm. */ if (use_keytab || keytab_str) { if (keytab_str) ret = krb5_kt_resolve(context, keytab_str, &kt); else ret = krb5_kt_default(context, &kt); if (ret) krb5_err(context, 1, ret, "resolving keytab"); } if (pk_enterprise_flag) { ret = krb5_pk_enterprise_cert(context, pk_user_id, argv[0], &principal, &ent_user_id); if (ret) krb5_err(context, 1, ret, "krb5_pk_enterprise_certs"); pk_user_id = NULL; } else if (anonymous_flag) { ret = krb5_make_principal(context, &principal, argv[0], KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, NULL); if (ret) krb5_err(context, 1, ret, "krb5_make_principal"); krb5_principal_set_type(context, principal, KRB5_NT_WELLKNOWN); } else if (use_keytab || keytab_str) { get_princ_kt(context, &principal, argv[0]); } else { get_princ(context, &principal, argv[0]); } if (fcache_version) krb5_set_fcache_version(context, fcache_version); if (renewable_flag == -1) /* this seems somewhat pointless, but whatever */ krb5_appdefault_boolean(context, "kinit", krb5_principal_get_realm(context, principal), "renewable", FALSE, &renewable_flag); if (do_afslog == -1) krb5_appdefault_boolean(context, "kinit", krb5_principal_get_realm(context, principal), "afslog", TRUE, &do_afslog); if (cred_cache) ret = krb5_cc_resolve(context, cred_cache, &ccache); else { if (argc > 1) { char s[1024]; ret = krb5_cc_new_unique(context, NULL, NULL, &ccache); if (ret) krb5_err(context, 1, ret, "creating cred cache"); snprintf(s, sizeof(s), "%s:%s", krb5_cc_get_type(context, ccache), krb5_cc_get_name(context, ccache)); setenv("KRB5CCNAME", s, 1); } else { ret = krb5_cc_cache_match(context, principal, &ccache); if (ret) { const char *type; ret = krb5_cc_default(context, &ccache); if (ret) krb5_err(context, 1, ret, N_("resolving credentials cache", "")); /* * Check if the type support switching, and we do, * then do that instead over overwriting the current * default credential */ type = krb5_cc_get_type(context, ccache); if (krb5_cc_support_switch(context, type)) { krb5_cc_close(context, ccache); ret = get_switched_ccache(context, type, principal, &ccache); } } } } if (ret) krb5_err(context, 1, ret, N_("resolving credentials cache", "")); #ifndef NO_AFS if (argc > 1 && k_hasafs()) k_setpag(); #endif if (lifetime) { int tmp = parse_time(lifetime, "s"); if (tmp < 0) errx(1, N_("unparsable time: %s", ""), lifetime); ticket_life = tmp; } if (addrs_flag == 0 && extra_addresses.num_strings > 0) krb5_errx(context, 1, N_("specifying both extra addresses and " "no addresses makes no sense", "")); { int i; krb5_addresses addresses; memset(&addresses, 0, sizeof(addresses)); for(i = 0; i < extra_addresses.num_strings; i++) { ret = krb5_parse_address(context, extra_addresses.strings[i], &addresses); if (ret == 0) { krb5_add_extra_addresses(context, &addresses); krb5_free_addresses(context, &addresses); } } free_getarg_strings(&extra_addresses); } if (renew_flag || validate_flag) { ret = renew_validate(context, renew_flag, validate_flag, ccache, server_str, ticket_life); #ifndef NO_AFS if (ret == 0 && server_str == NULL && do_afslog && k_hasafs()) krb5_afslog(context, ccache, NULL, NULL); #endif exit(ret != 0); } ret = get_new_tickets(context, principal, ccache, ticket_life, 1); if (ret) exit(1); #ifndef NO_AFS if (ret == 0 && server_str == NULL && do_afslog && k_hasafs()) krb5_afslog(context, ccache, NULL, NULL); #endif if (argc > 1) { struct renew_ctx ctx; time_t timeout; timeout = ticket_lifetime(context, ccache, principal, server_str, NULL) / 2; ctx.context = context; ctx.ccache = ccache; ctx.principal = principal; ctx.ticket_life = ticket_life; ctx.timeout = timeout; #ifdef HAVE_SIGACTION memset(&sa, 0, sizeof(sa)); sigemptyset(&sa.sa_mask); sa.sa_handler = handle_siginfo; sigaction(SIGINFO, &sa, NULL); #endif ret = simple_execvp_timed(argv[1], argv+1, renew_func, &ctx, timeout); #define EX_NOEXEC 126 #define EX_NOTFOUND 127 if (ret == EX_NOEXEC) krb5_warnx(context, N_("permission denied: %s", ""), argv[1]); else if (ret == EX_NOTFOUND) krb5_warnx(context, N_("command not found: %s", ""), argv[1]); krb5_cc_destroy(context, ccache); #ifndef NO_AFS if (k_hasafs()) k_unlog(); #endif } else { krb5_cc_close(context, ccache); ret = 0; } krb5_free_principal(context, principal); if (kt) krb5_kt_close(context, kt); krb5_free_context(context); return ret; }
static int send_complete (krb5_context context, slave *s, const char *database, uint32_t current_version, uint32_t oldest_version, uint32_t initial_log_tstamp) { krb5_error_code ret; krb5_storage *dump = NULL; uint32_t vno = 0; krb5_data data; int fd = -1; struct stat st; char *dfn; ret = asprintf(&dfn, "%s/ipropd.dumpfile", hdb_db_dir(context)); if (ret == -1 || !dfn) { krb5_warn(context, ENOMEM, "Cannot allocate memory"); return ENOMEM; } fd = open(dfn, O_CREAT|O_RDWR, 0600); if (fd == -1) { ret = errno; krb5_warn(context, ret, "Cannot open/create iprop dumpfile %s", dfn); free(dfn); return ret; } free(dfn); dump = krb5_storage_from_fd(fd); if (!dump) { ret = errno; krb5_warn(context, ret, "krb5_storage_from_fd"); goto done; } for (;;) { ret = flock(fd, LOCK_SH); if (ret == -1) { ret = errno; krb5_warn(context, ret, "flock(fd, LOCK_SH)"); goto done; } if (krb5_storage_seek(dump, 0, SEEK_SET) == (off_t)-1) { ret = errno; krb5_warn(context, ret, "krb5_storage_seek(dump, 0, SEEK_SET)"); goto done; } vno = 0; ret = krb5_ret_uint32(dump, &vno); if (ret && ret != HEIM_ERR_EOF) { krb5_warn(context, ret, "krb5_ret_uint32(dump, &vno)"); goto done; } if (fstat(fd, &st) == -1) { ret = errno; krb5_warn(context, ret, "send_complete: could not stat dump file"); goto done; } /* * If the current dump has an appropriate version, then we can * break out of the loop and send the file below. */ if (ret == 0 && vno != 0 && st.st_mtime > initial_log_tstamp && vno >= oldest_version && vno <= current_version) break; if (verbose) krb5_warnx(context, "send_complete: dumping HDB"); /* * Otherwise, we may need to write a new dump file. We * obtain an exclusive lock on the fd. Because this is * not guaranteed to be an upgrade of our existing shared * lock, someone else may have written a new dumpfile while * we were waiting and so we must first check the vno of * the dump to see if that happened. If it did, we need * to go back to the top of the loop so that we can downgrade * our lock to a shared one. */ ret = flock(fd, LOCK_EX); if (ret == -1) { ret = errno; krb5_warn(context, ret, "flock(fd, LOCK_EX)"); goto done; } ret = krb5_storage_seek(dump, 0, SEEK_SET); if (ret == -1) { ret = errno; krb5_warn(context, ret, "krb5_storage_seek(dump, 0, SEEK_SET)"); goto done; } vno = 0; ret = krb5_ret_uint32(dump, &vno); if (ret && ret != HEIM_ERR_EOF) { krb5_warn(context, ret, "krb5_ret_uint32(dump, &vno)"); goto done; } if (fstat(fd, &st) == -1) { ret = errno; krb5_warn(context, ret, "send_complete: could not stat dump file"); goto done; } /* check if someone wrote a better version for us */ if (ret == 0 && vno != 0 && st.st_mtime > initial_log_tstamp && vno >= oldest_version && vno <= current_version) continue; /* Now, we know that we must write a new dump file. */ ret = write_dump(context, dump, database, current_version); if (ret) goto done; /* * And we must continue to the top of the loop so that we can * downgrade to a shared lock. */ } /* * Leaving the above loop, dump should have a ptr right after the initial * 4 byte DB version number and we should have a shared lock on the file * (which we may have just created), so we are reading to simply blast * the data down the wire. */ for (;;) { ret = krb5_ret_data(dump, &data); if (ret == HEIM_ERR_EOF) { ret = 0; /* EOF is not an error, it's success */ goto done; } if (ret) { krb5_warn(context, ret, "krb5_ret_data(dump, &data)"); slave_dead(context, s); goto done; } ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); krb5_data_free(&data); if (ret) { krb5_warn (context, ret, "krb5_write_priv_message"); slave_dead(context, s); goto done; } } done: if (!ret) { s->version = vno; slave_seen(s); } if (fd != -1) close(fd); if (dump) krb5_storage_free(dump); return ret; }
static time_t renew_func(void *ptr) { krb5_error_code ret; struct renew_ctx *ctx = ptr; time_t renew_expire; static time_t exp_delay = 1; /* * NOTE: We count on the ccache implementation to notice changes to the * actual ccache filesystem/whatever objects. There should be no ccache * types for which this is not the case, but it might not hurt to * re-krb5_cc_resolve() after each successful renew_validate()/ * get_new_tickets() call. */ expire = ticket_lifetime(ctx->context, ctx->ccache, ctx->principal, server_str, &renew_expire); /* * When a keytab is available to obtain new tickets, if we are within * half of the original ticket lifetime of the renew limit, get a new * TGT instead of renewing the existing TGT. Note, ctx->ticket_life * is zero by default (without a '-l' option) and cannot be used to * set the time scale on which we decide whether we're "close to the * renew limit". */ if (use_keytab || keytab_str) expire += ctx->timeout; if (renew_expire > expire) { ret = renew_validate(ctx->context, 1, validate_flag, ctx->ccache, server_str, ctx->ticket_life); } else { ret = get_new_tickets(ctx->context, ctx->principal, ctx->ccache, ctx->ticket_life, 0); } expire = ticket_lifetime(ctx->context, ctx->ccache, ctx->principal, server_str, &renew_expire); #ifndef NO_AFS if (ret == 0 && server_str == NULL && do_afslog && k_hasafs()) krb5_afslog(ctx->context, ctx->ccache, NULL, NULL); #endif update_siginfo_msg(expire, server_str); /* * If our tickets have expired and we been able to either renew them * or obtain new tickets, then we still call this function but we use * an exponential backoff. This should take care of the case where * we are using stored credentials but the KDC has been unavailable * for some reason... */ if (expire < 1) { /* * We can't ask to keep spamming stderr but not syslog, so we warn * only once. */ if (exp_delay == 1) { krb5_warnx(ctx->context, N_("NOTICE: Could not renew/refresh " "tickets", "")); } if (exp_delay < 7200) exp_delay += exp_delay / 2 + 1; return exp_delay; } exp_delay = 1; return expire / 2 + 1; }
static int send_diffs (kadm5_server_context *server_context, slave *s, int log_fd, const char *database, uint32_t current_version, uint32_t current_tstamp) { krb5_context context = server_context->context; krb5_storage *sp; uint32_t ver, initial_version, initial_version2; uint32_t initial_tstamp, initial_tstamp2; enum kadm_ops op; uint32_t len; off_t right, left; krb5_ssize_t bytes; krb5_data data; int ret = 0; if (s->flags & SLAVE_F_DEAD) { krb5_warnx(context, "not sending diffs to dead slave %s", s->name); return 0; } if (s->version == current_version) { char buf[4]; sp = krb5_storage_from_mem(buf, 4); if (sp == NULL) krb5_errx(context, IPROPD_RESTART, "krb5_storage_from_mem"); ret = krb5_store_uint32(sp, YOU_HAVE_LAST_VERSION); krb5_storage_free(sp); data.data = buf; data.length = 4; if (ret == 0) { ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); if (ret) { krb5_warn(context, ret, "send_diffs: failed to send to slave"); slave_dead(context, s); } krb5_warnx(context, "slave %s in sync already at version %ld", s->name, (long)s->version); } return ret; } if (verbose) krb5_warnx(context, "sending diffs to live-seeming slave %s", s->name); /* * XXX The code that makes the diffs should be made a separate function, * then error handling (send_are_you_there() or slave_dead()) can be done * here. */ if (flock(log_fd, LOCK_SH) == -1) { krb5_warn(context, errno, "could not obtain shared lock on log file"); send_are_you_there(context, s); return errno; } ret = kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_FIRST, &initial_version, &initial_tstamp); sp = kadm5_log_goto_end(server_context, log_fd); flock(log_fd, LOCK_UN); if (ret) { if (sp != NULL) krb5_storage_free(sp); krb5_warn(context, ret, "send_diffs: failed to read log"); send_are_you_there(context, s); return ret; } if (sp == NULL) { send_are_you_there(context, s); krb5_warn(context, errno ? errno : EINVAL, "send_diffs: failed to read log"); return errno ? errno : EINVAL; } /* * We're not holding any locks here, so we can't prevent truncations. * * We protect against this by re-checking that the initial version and * timestamp are the same before and after this loop. */ right = krb5_storage_seek(sp, 0, SEEK_CUR); if (right == (off_t)-1) { krb5_storage_free(sp); send_are_you_there(context, s); return errno; } for (;;) { ret = kadm5_log_previous (context, sp, &ver, NULL, &op, &len); if (ret) krb5_err(context, IPROPD_RESTART, ret, "send_diffs: failed to find previous entry"); left = krb5_storage_seek(sp, -16, SEEK_CUR); if (left == (off_t)-1) { krb5_storage_free(sp); send_are_you_there(context, s); return errno; } if (ver == s->version + 1) break; /* * We don't expect to reach the slave's version, except when it is * starting empty with the uber record. */ if (ver == s->version && !(ver == 0 && op == kadm_nop)) { /* * This shouldn't happen, but recall we're not holding a lock on * the log. */ krb5_storage_free(sp); krb5_warnx(context, "iprop log truncated while sending diffs to " "slave?? ver = %lu", (unsigned long)ver); send_are_you_there(context, s); return 0; } /* If we've reached the uber record, send the complete database */ if (left == 0 || (ver == 0 && op == kadm_nop)) { krb5_storage_free(sp); krb5_warnx(context, "slave %s (version %lu) out of sync with master " "(first version in log %lu), sending complete database", s->name, (unsigned long)s->version, (unsigned long)ver); return send_complete (context, s, database, current_version, ver, initial_tstamp); } } assert(ver == s->version + 1); krb5_warnx(context, "syncing slave %s from version %lu to version %lu", s->name, (unsigned long)s->version, (unsigned long)current_version); ret = krb5_data_alloc (&data, right - left + 4); if (ret) { krb5_storage_free(sp); krb5_warn (context, ret, "send_diffs: krb5_data_alloc"); send_are_you_there(context, s); return 1; } bytes = krb5_storage_read(sp, (char *)data.data + 4, data.length - 4); krb5_storage_free(sp); if (bytes != data.length - 4) { krb5_warnx(context, "iprop log truncated while sending diffs to " "slave?? ver = %lu", (unsigned long)ver); send_are_you_there(context, s); return 1; } /* * Check that we have the same log initial version and timestamp now as * when we dropped the shared lock on the log file! Else we could be * sending garbage to the slave. */ if (flock(log_fd, LOCK_SH) == -1) { krb5_warn(context, errno, "could not obtain shared lock on log file"); send_are_you_there(context, s); return 1; } ret = kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_FIRST, &initial_version2, &initial_tstamp2); flock(log_fd, LOCK_UN); if (ret) { krb5_warn(context, ret, "send_diffs: failed to read log while producing diffs"); send_are_you_there(context, s); return 1; } if (initial_version != initial_version2 || initial_tstamp != initial_tstamp2) { krb5_warn(context, ret, "send_diffs: log truncated while producing diffs"); send_are_you_there(context, s); return 1; } sp = krb5_storage_from_data (&data); if (sp == NULL) { krb5_warnx (context, "send_diffs: krb5_storage_from_data"); send_are_you_there(context, s); return 1; } krb5_store_uint32 (sp, FOR_YOU); krb5_storage_free(sp); ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); krb5_data_free(&data); if (ret) { krb5_warn (context, ret, "send_diffs: krb5_write_priv_message"); slave_dead(context, s); return 1; } slave_seen(s); s->version = current_version; krb5_warnx(context, "slave %s is now up to date (%u)", s->name, s->version); return 0; }