Example #1
0
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);
    }
}
Example #2
0
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;
}
Example #3
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;
}
Example #4
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;
}
Example #5
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;
}
Example #6
0
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);
}
Example #7
0
/**
 * 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;
}
Example #8
0
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, &timestamp, &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;
}
Example #9
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);
}
Example #10
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;
}
Example #11
0
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, &timestamp);
	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, &timestamp);
	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;
}
Example #12
0
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 */
}
Example #13
0
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;
    }
}
Example #14
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;
}
Example #15
0
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"));
}
Example #16
0
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;
}
Example #17
0
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;
}
Example #18
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;
}
Example #19
0
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, &params);

	ret = krb5_write_priv_message(context, ctx->ac, &s, &params);
	krb5_data_free(&params);
	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;
}
Example #20
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;
}
Example #21
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);
}
Example #22
0
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;
}
Example #23
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;
}
Example #24
0
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,
                             &current_version, &current_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,
                                     &current_version, &current_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,
                                     &current_version, &current_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,
                                     &current_version, &current_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;
}
Example #25
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;
}
Example #26
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;
}
Example #27
0
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;
}
Example #28
0
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;
}
Example #29
0
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;
}
Example #30
0
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;
}