Example #1
0
static void*
open_kadmin_connection(char *principal,
		       const char *realm,
		       char *admin_server,
		       int server_port)
{
    static kadm5_config_params conf;
    krb5_error_code ret;
    void *kadm_handle;
    memset(&conf, 0, sizeof(conf));

    if(realm) {
	conf.realm = strdup(realm);
	if (conf.realm == NULL) {
	    krb5_set_error_message(context, 0, "malloc: out of memory");
	    return NULL;
	}
	conf.mask |= KADM5_CONFIG_REALM;
    }

    if (admin_server) {
	conf.admin_server = admin_server;
	conf.mask |= KADM5_CONFIG_ADMIN_SERVER;
    }

    if (server_port) {
	conf.kadmind_port = htons(server_port);
	conf.mask |= KADM5_CONFIG_KADMIND_PORT;
    }

    /* should get realm from each principal, instead of doing
       everything with the same (local) realm */

    ret = kadm5_init_with_password_ctx(context,
				       principal,
				       NULL,
				       KADM5_ADMIN_SERVICE,
				       &conf, 0, 0,
				       &kadm_handle);
    free(conf.realm);
    if(ret) {
	krb5_warn(context, ret, "kadm5_init_with_password");
	return NULL;
    }
    return kadm_handle;
}
Example #2
0
static kadm5_server_context *
get_kadmin_context(const char *config_file, char *realm)
{
    kadm5_config_params conf;
    krb5_error_code ret;
    void *kadm_handle;
    char **files;
    int aret;

    if (config_file == NULL) {
	char *file;
	aret = asprintf(&file, "%s/kdc.conf", hdb_db_dir(context));
	if (aret == -1 || file == NULL)
	    errx(1, "out of memory");
	config_file = file;
    }

    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");

    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");

    return (kadm5_server_context *)kadm_handle;
}
Example #3
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 #4
0
static void
change (krb5_auth_context auth_context,
        krb5_principal admin_principal,
        uint16_t version,
        int s,
        struct sockaddr *sa,
        int sa_size,
        krb5_data *in_data)
{
    krb5_error_code ret;
    char *client = NULL, *admin = NULL;
    const char *pwd_reason;
    kadm5_config_params conf;
    void *kadm5_handle = NULL;
    krb5_principal principal = NULL;
    krb5_data *pwd_data = NULL;
    char *tmp;
    ChangePasswdDataMS chpw;

    memset (&conf, 0, sizeof(conf));
    memset(&chpw, 0, sizeof(chpw));

    if (version == KRB5_KPASSWD_VERS_CHANGEPW) {
        ret = krb5_copy_data(context, in_data, &pwd_data);
        if (ret) {
            krb5_warn (context, ret, "krb5_copy_data");
            reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED,
                        "out out memory copying password");
            return;
        }
        principal = admin_principal;
    } else if (version == KRB5_KPASSWD_VERS_SETPW) {
        size_t len;

        ret = decode_ChangePasswdDataMS(in_data->data, in_data->length,
                                        &chpw, &len);
        if (ret) {
            krb5_warn (context, ret, "decode_ChangePasswdDataMS");
            reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED,
                        "malformed ChangePasswdData");
            return;
        }


        ret = krb5_copy_data(context, &chpw.newpasswd, &pwd_data);
        if (ret) {
            krb5_warn (context, ret, "krb5_copy_data");
            reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED,
                        "out out memory copying password");
            goto out;
        }

        if (chpw.targname == NULL && chpw.targrealm != NULL) {
            krb5_warn (context, ret, "kadm5_init_with_password_ctx");
            reply_priv (auth_context, s, sa, sa_size,
                        KRB5_KPASSWD_MALFORMED,
                        "targrealm but not targname");
            goto out;
        }

        if (chpw.targname) {
            krb5_principal_data princ;

            princ.name = *chpw.targname;
            princ.realm = *chpw.targrealm;
            if (princ.realm == NULL) {
                ret = krb5_get_default_realm(context, &princ.realm);

                if (ret) {
                    krb5_warnx (context,
                                "kadm5_init_with_password_ctx: "
                                "failed to allocate realm");
                    reply_priv (auth_context, s, sa, sa_size,
                                KRB5_KPASSWD_SOFTERROR,
                                "failed to allocate realm");
                    goto out;
                }
            }
            ret = krb5_copy_principal(context, &princ, &principal);
            if (*chpw.targrealm == NULL)
                free(princ.realm);
            if (ret) {
                krb5_warn(context, ret, "krb5_copy_principal");
                reply_priv(auth_context, s, sa, sa_size,
                           KRB5_KPASSWD_HARDERROR,
                           "failed to allocate principal");
                goto out;
            }
        } else
            principal = admin_principal;
    } else {
        krb5_warnx (context, "kadm5_init_with_password_ctx: unknown proto");
        reply_priv (auth_context, s, sa, sa_size,
                    KRB5_KPASSWD_HARDERROR,
                    "Unknown protocol used");
        return;
    }

    ret = krb5_unparse_name (context, admin_principal, &admin);
    if (ret) {
        krb5_warn (context, ret, "unparse_name failed");
        reply_priv (auth_context, s, sa, sa_size,
                    KRB5_KPASSWD_HARDERROR, "out of memory error");
        goto out;
    }

    conf.realm = principal->realm;
    conf.mask |= KADM5_CONFIG_REALM;

    ret = kadm5_init_with_password_ctx(context,
                                       admin,
                                       NULL,
                                       KADM5_ADMIN_SERVICE,
                                       &conf, 0, 0,
                                       &kadm5_handle);
    if (ret) {
        krb5_warn (context, ret, "kadm5_init_with_password_ctx");
        reply_priv (auth_context, s, sa, sa_size, 2,
                    "Internal error");
        goto out;
    }

    ret = krb5_unparse_name(context, principal, &client);
    if (ret) {
        krb5_warn (context, ret, "unparse_name failed");
        reply_priv (auth_context, s, sa, sa_size,
                    KRB5_KPASSWD_HARDERROR, "out of memory error");
        goto out;
    }

    /*
     * Check password quality if not changing as administrator
     */

    if (krb5_principal_compare(context, admin_principal, principal) == TRUE) {

        pwd_reason = kadm5_check_password_quality (context, principal,
                     pwd_data);
        if (pwd_reason != NULL ) {
            krb5_warnx (context,
                        "%s didn't pass password quality check with error: %s",
                        client, pwd_reason);
            reply_priv (auth_context, s, sa, sa_size,
                        KRB5_KPASSWD_SOFTERROR, pwd_reason);
            goto out;
        }
        krb5_warnx (context, "Changing password for %s", client);
    } else {
        ret = _kadm5_acl_check_permission(kadm5_handle, KADM5_PRIV_CPW,
                                          principal);
        if (ret) {
            krb5_warn (context, ret,
                       "Check ACL failed for %s for changing %s password",
                       admin, client);
            reply_priv (auth_context, s, sa, sa_size,
                        KRB5_KPASSWD_HARDERROR, "permission denied");
            goto out;
        }
        krb5_warnx (context, "%s is changing password for %s", admin, client);
    }

    ret = krb5_data_realloc(pwd_data, pwd_data->length + 1);
    if (ret) {
        krb5_warn (context, ret, "malloc: out of memory");
        reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_HARDERROR,
                    "Internal error");
        goto out;
    }
    tmp = pwd_data->data;
    tmp[pwd_data->length - 1] = '\0';

    ret = kadm5_s_chpass_principal_cond (kadm5_handle, principal, tmp);
    krb5_free_data (context, pwd_data);
    pwd_data = NULL;
    if (ret) {
        const char *str = krb5_get_error_message(context, ret);
        krb5_warnx(context, "kadm5_s_chpass_principal_cond: %s", str);
        reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_SOFTERROR,
                    str ? str : "Internal error");
        krb5_free_error_message(context, str);
        goto out;
    }
    reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_SUCCESS,
                "Password changed");
out:
    free_ChangePasswdDataMS(&chpw);
    if (principal != admin_principal)
        krb5_free_principal(context, principal);
    if (admin)
        free(admin);
    if (client)
        free(client);
    if (pwd_data)
        krb5_free_data(context, pwd_data);
    if (kadm5_handle)
        kadm5_destroy (kadm5_handle);
}
Example #5
0
int
main(int argc, char **argv)
{
    krb5_error_code ret, ret2;
    krb5_context context;
    krb5_auth_context auth_context;
    void *kadm_handle;
    kadm5_server_context *server_context;
    kadm5_config_params conf;
    int master_fd;
    krb5_ccache ccache;
    krb5_principal server;
    char **files;
    int optidx = 0;
    time_t reconnect_min;
    time_t backoff;
    time_t reconnect_max;
    time_t reconnect;
    time_t before = 0;
    int restarter_fd = -1;

    const char *master;

    setprogname(argv[0]);

    if (getarg(args, num_args, argc, argv, &optidx))
	usage(1);

    if (help_flag)
	usage(0);

    if (version_flag) {
	print_version(NULL);
	exit(0);
    }

    if (detach_from_console && daemon_child == -1)
        roken_detach_prep(argc, argv, "--daemon-child");
    rk_pidfile(NULL);

    ret = krb5_init_context(&context);
    if (ret)
	errx (1, "krb5_init_context failed: %d", ret);

    setup_signal();

    if (config_file == NULL) {
	if (asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context)) == -1
	    || config_file == NULL)
	    errx(1, "out of memory");
    }

    ret = krb5_prepend_config_files_default(config_file, &files);
    if (ret)
	krb5_err(context, 1, ret, "getting configuration files");

    ret = krb5_set_config_files(context, files);
    krb5_free_config_files(files);
    if (ret)
	krb5_err(context, 1, ret, "reading configuration files");

    argc -= optidx;
    argv += optidx;

    if (argc != 1)
	usage(1);

    master = argv[0];

    if (status_file == NULL) {
	if (asprintf(&status_file,  "%s/ipropd-slave-status", hdb_db_dir(context)) < 0 || status_file == NULL)
	    krb5_errx(context, 1, "can't allocate status file buffer"); 
    }

    krb5_openlog(context, "ipropd-slave", &log_facility);
    krb5_set_warn_dest(context, log_facility);

    slave_status(context, status_file, "bootstrapping");

    ret = krb5_kt_register(context, &hdb_get_kt_ops);
    if(ret)
	krb5_err(context, 1, ret, "krb5_kt_register");

    time_before_lost = parse_time (server_time_lost,  "s");
    if (time_before_lost < 0)
	krb5_errx (context, 1, "couldn't parse time: %s", server_time_lost);

    slave_status(context, status_file, "getting credentials from keytab/database");

    memset(&conf, 0, sizeof(conf));
    if(realm) {
	conf.mask |= KADM5_CONFIG_REALM;
	conf.realm = realm;
    }
    ret = kadm5_init_with_password_ctx (context,
					KADM5_ADMIN_SERVICE,
					NULL,
					KADM5_ADMIN_SERVICE,
					&conf, 0, 0,
					&kadm_handle);
    if (ret)
	krb5_err (context, 1, ret, "kadm5_init_with_password_ctx");

    server_context = (kadm5_server_context *)kadm_handle;

    slave_status(context, status_file, "creating log file");

    ret = server_context->db->hdb_open(context,
                                       server_context->db,
                                       O_RDWR | O_CREAT, 0600);
    if (ret)
	krb5_err (context, 1, ret, "db->open");

    ret = kadm5_log_init (server_context);
    if (ret)
	krb5_err (context, 1, ret, "kadm5_log_init");

    ret = server_context->db->hdb_close (context, server_context->db);
    if (ret)
	krb5_err (context, 1, ret, "db->close");

    get_creds(context, keytab_str, &ccache, master);

    ret = krb5_sname_to_principal (context, master, IPROP_NAME,
				   KRB5_NT_SRV_HST, &server);
    if (ret)
	krb5_err (context, 1, ret, "krb5_sname_to_principal");

    auth_context = NULL;
    master_fd = -1;

    krb5_appdefault_time(context, config_name, NULL, "reconnect-min",
			 10, &reconnect_min);
    krb5_appdefault_time(context, config_name, NULL, "reconnect-max",
			 300, &reconnect_max);
    krb5_appdefault_time(context, config_name, NULL, "reconnect-backoff",
			 10, &backoff);
    reconnect = reconnect_min;

    slave_status(context, status_file, "ipropd-slave started");

    roken_detach_finish(NULL, daemon_child);
    restarter_fd = restarter(context, NULL);

    while (!exit_flag) {
        struct timeval to;
	time_t now, elapsed;
        fd_set readset;
	int connected = FALSE;

#ifndef NO_LIMIT_FD_SETSIZE
        if (restarter_fd >= FD_SETSIZE)
            krb5_errx(context, IPROPD_RESTART, "fd too large");
#endif

        FD_ZERO(&readset);
        if (restarter_fd > -1)
            FD_SET(restarter_fd, &readset);

	now = time(NULL);
	elapsed = now - before;

	if (elapsed < reconnect) {
	    time_t left = reconnect - elapsed;
	    krb5_warnx(context, "sleeping %d seconds before "
		       "retrying to connect", (int)left);
            to.tv_sec = left;
            to.tv_usec = 0;
            if (select(restarter_fd + 1, &readset, NULL, NULL, &to) == 1) {
                exit_flag = SIGTERM;
                continue;
            }
	}
	before = now;

	slave_status(context, status_file, "connecting to master: %s\n", master);

	master_fd = connect_to_master (context, master, port_str);
	if (master_fd < 0)
	    goto retry;

	reconnect = reconnect_min;

	if (auth_context) {
	    krb5_auth_con_free(context, auth_context);
	    auth_context = NULL;
	    krb5_cc_destroy(context, ccache);
	    get_creds(context, keytab_str, &ccache, master);
	}
        if (verbose)
            krb5_warnx(context, "authenticating to master");
	ret = krb5_sendauth (context, &auth_context, &master_fd,
			     IPROP_VERSION, NULL, server,
			     AP_OPTS_MUTUAL_REQUIRED, NULL, NULL,
			     ccache, NULL, NULL, NULL);
	if (ret) {
	    krb5_warn (context, ret, "krb5_sendauth");
	    goto retry;
	}

	krb5_warnx(context, "ipropd-slave started at version: %ld",
		   (long)server_context->log_context.version);

	ret = ihave(context, auth_context, master_fd,
		    server_context->log_context.version);
	if (ret)
	    goto retry;

	connected = TRUE;

        if (verbose)
            krb5_warnx(context, "connected to master");

	slave_status(context, status_file, "connected to master, waiting instructions");

	while (connected && !exit_flag) {
	    krb5_data out;
	    krb5_storage *sp;
	    uint32_t tmp;
            int max_fd;

#ifndef NO_LIMIT_FD_SETSIZE
	    if (master_fd >= FD_SETSIZE)
                krb5_errx(context, IPROPD_RESTART, "fd too large");
            if (restarter_fd >= FD_SETSIZE)
                krb5_errx(context, IPROPD_RESTART, "fd too large");
            max_fd = max(restarter_fd, master_fd);
#endif

	    FD_ZERO(&readset);
	    FD_SET(master_fd, &readset);
            if (restarter_fd != -1)
                FD_SET(restarter_fd, &readset);

	    to.tv_sec = time_before_lost;
	    to.tv_usec = 0;

	    ret = select (max_fd + 1,
			  &readset, NULL, NULL, &to);
	    if (ret < 0) {
		if (errno == EINTR)
		    continue;
		else
		    krb5_err (context, 1, errno, "select");
	    }
	    if (ret == 0) {
		krb5_warnx(context, "server didn't send a message "
                           "in %d seconds", time_before_lost);
		connected = FALSE;
		continue;
	    }

            if (restarter_fd > -1 && FD_ISSET(restarter_fd, &readset)) {
                if (verbose)
                    krb5_warnx(context, "slave restarter exited");
                exit_flag = SIGTERM;
            }

            if (!FD_ISSET(master_fd, &readset))
                continue;

            if (verbose)
                krb5_warnx(context, "message from master");

	    ret = krb5_read_priv_message(context, auth_context, &master_fd, &out);
	    if (ret) {
		krb5_warn(context, ret, "krb5_read_priv_message");
		connected = FALSE;
		continue;
	    }

	    sp = krb5_storage_from_mem (out.data, out.length);
            if (sp == NULL)
                krb5_err(context, IPROPD_RESTART, errno, "krb5_storage_from_mem");
	    ret = krb5_ret_uint32(sp, &tmp);
            if (ret == HEIM_ERR_EOF) {
                krb5_warn(context, ret, "master sent zero-length message");
                connected = FALSE;
                continue;
            }
            if (ret != 0) {
                krb5_warn(context, ret, "couldn't read master's message");
                connected = FALSE;
                continue;
            }

	    ret = server_context->db->hdb_open(context,
					       server_context->db,
					       O_RDWR | O_CREAT, 0600);
	    if (ret)
		krb5_err (context, 1, ret, "db->open while handling a "
			  "message from the master");

            ret = kadm5_log_init(server_context);
            if (ret) {
                krb5_err(context, IPROPD_RESTART, ret, "kadm5_log_init while "
                         "handling a message from the master");
            }
	    ret = server_context->db->hdb_close (context, server_context->db);
	    if (ret)
		krb5_err (context, 1, ret, "db->close while handling a "
			  "message from the master");

	    switch (tmp) {
	    case FOR_YOU :
                if (verbose)
                    krb5_warnx(context, "master sent us diffs");
		ret2 = receive(context, sp, server_context);
                if (ret2)
                    krb5_warn(context, ret2,
                              "receive from ipropd-master had errors");
		ret = ihave(context, auth_context, master_fd,
			    server_context->log_context.version);
		if (ret || ret2)
		    connected = FALSE;

                /*
                 * If it returns an error, receive() may nonetheless
                 * have committed some entries successfully, so we must
                 * update the slave_status even if there were errors.
                 */
                is_up_to_date(context, status_file, server_context);
		break;
	    case TELL_YOU_EVERYTHING :
                if (verbose)
                    krb5_warnx(context, "master sent us a full dump");
		ret = receive_everything(context, master_fd, server_context,
					 auth_context);
                if (ret == 0) {
                    ret = ihave(context, auth_context, master_fd,
                                server_context->log_context.version);
                }
                if (ret)
		    connected = FALSE;
                else
                    is_up_to_date(context, status_file, server_context);
		break;
	    case ARE_YOU_THERE :
                if (verbose)
                    krb5_warnx(context, "master sent us a ping");
		is_up_to_date(context, status_file, server_context);
                ret = ihave(context, auth_context, master_fd,
                            server_context->log_context.version);
                if (ret)
                    connected = FALSE;

		send_im_here(context, master_fd, auth_context);
		break;
	    case YOU_HAVE_LAST_VERSION:
                if (verbose)
                    krb5_warnx(context, "master tells us we are up to date");
		is_up_to_date(context, status_file, server_context);
		break;
	    case NOW_YOU_HAVE :
	    case I_HAVE :
	    case ONE_PRINC :
	    case I_AM_HERE :
	    default :
		krb5_warnx (context, "Ignoring command %d", tmp);
		break;
	    }
	    krb5_storage_free (sp);
	    krb5_data_free (&out);

	}

	slave_status(context, status_file, "disconnected from master");
    retry:
	if (connected == FALSE)
	    krb5_warnx (context, "disconnected for server");

	if (exit_flag)
	    krb5_warnx (context, "got an exit signal");

	if (master_fd >= 0)
	    close(master_fd);

	reconnect += backoff;
	if (reconnect > reconnect_max) {
	    slave_status(context, status_file, "disconnected from master for a long time");
	    reconnect = reconnect_max;
	}
    }

    if (status_file) {
        /* XXX It'd be better to leave it saying we're not here */
	unlink(status_file);
    }

    if (0);
#ifndef NO_SIGXCPU
    else if(exit_flag == SIGXCPU)
	krb5_warnx(context, "%s CPU time limit exceeded", getprogname());
#endif
    else if(exit_flag == SIGINT || exit_flag == SIGTERM)
	krb5_warnx(context, "%s terminated", getprogname());
    else
	krb5_warnx(context, "%s unexpected exit reason: %ld",
		       getprogname(), (long)exit_flag);

    return 0;
}