Example #1
0
static int
dump_one (krb5_context context, HDB *db, hdb_entry_ex *entry, void *v)
{
    krb5_error_code ret;
    krb5_storage *dump = (krb5_storage *)v;
    krb5_storage *sp;
    krb5_data data;

    ret = hdb_entry2value (context, &entry->entry, &data);
    if (ret)
	return ret;
    ret = krb5_data_realloc (&data, data.length + 4);
    if (ret)
	goto done;
    memmove ((char *)data.data + 4, data.data, data.length - 4);
    sp = krb5_storage_from_data(&data);
    if (sp == NULL) {
	ret = ENOMEM;
	goto done;
    }
    ret = krb5_store_uint32(sp, ONE_PRINC);
    krb5_storage_free(sp);

    if (ret == 0)
        ret = krb5_store_data(dump, data);

done:
    krb5_data_free (&data);
    return ret;
}
Example #2
0
static void
send_im_here(krb5_context context, int fd,
	     krb5_auth_context auth_context)
{
    krb5_storage *sp;
    krb5_data data;
    krb5_error_code ret;

    ret = krb5_data_alloc(&data, 4);
    if (ret)
        krb5_err(context, IPROPD_RESTART, ret, "send_im_here");

    sp = krb5_storage_from_data (&data);
    if (sp == NULL)
        krb5_errx(context, IPROPD_RESTART, "krb5_storage_from_data");
    ret = krb5_store_uint32(sp, I_AM_HERE);
    krb5_storage_free(sp);

    if (ret == 0) {
        ret = krb5_write_priv_message(context, auth_context, &fd, &data);
        krb5_data_free(&data);

        if (ret)
            krb5_err(context, IPROPD_RESTART, ret, "krb5_write_priv_message");

        if (verbose)
            krb5_warnx(context, "pinged master");
    }

    return;
}
Example #3
0
static int
prop_one (krb5_context context, HDB *db, hdb_entry *entry, void *v)
{
    krb5_error_code ret;
    krb5_storage *sp;
    krb5_data data;
    struct slave *slave = (struct slave *)v;

    ret = hdb_entry2value (context, entry, &data);
    if (ret)
	return ret;
    ret = krb5_data_realloc (&data, data.length + 4);
    if (ret) {
	krb5_data_free (&data);
	return ret;
    }
    memmove ((char *)data.data + 4, data.data, data.length - 4);
    sp = krb5_storage_from_data(&data);
    if (sp == NULL) {
	krb5_data_free (&data);
	return ENOMEM;
    }
    krb5_store_int32(sp, ONE_PRINC);
    krb5_storage_free(sp);

    ret = krb5_write_priv_message (context, slave->ac, &slave->fd, &data);
    krb5_data_free (&data);
    return ret;
}
Example #4
0
kadm5_ret_t
_kadm5_unmarshal_params(krb5_context context,
			krb5_data *in,
			kadm5_config_params *params)
{
    krb5_error_code ret;
    krb5_storage *sp;
    int32_t mask;

    sp = krb5_storage_from_data(in);
    if (sp == NULL)
	return ENOMEM;

    ret = krb5_ret_int32(sp, &mask);
    if (ret)
	goto out;
    params->mask = mask;
	
    if(params->mask & KADM5_CONFIG_REALM)
	ret = krb5_ret_string(sp, &params->realm);
 out:
    krb5_storage_free(sp);

    return ret;
}
Example #5
0
krb5_error_code
kcm_dispatch(krb5_context context,
	     kcm_client *client,
	     krb5_data *req_data,
	     krb5_data *resp_data)
{
    krb5_error_code ret;
    kcm_method method;
    krb5_storage *req_sp = NULL;
    krb5_storage *resp_sp = NULL;
    u_int16_t opcode;

    resp_sp = krb5_storage_emem();
    if (resp_sp == NULL) {
	return ENOMEM;
    }

    if (client->pid == -1) {
	kcm_log(0, "Client had invalid process number");
	ret = KRB5_FCC_INTERNAL;
	goto out;
    }

    req_sp = krb5_storage_from_data(req_data);
    if (req_sp == NULL) {
	kcm_log(0, "Process %d: failed to initialize storage from data",
		client->pid);
	ret = KRB5_CC_IO;
	goto out;
    }

    krb5_ret_int16(req_sp, &opcode);

    if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) {
	kcm_log(0, "Process %d: invalid operation code %d",
		client->pid, opcode);
	ret = KRB5_FCC_INTERNAL;
	goto out;
    }
    method = kcm_ops[opcode].method;

    /* seek past place for status code */
    krb5_storage_seek(resp_sp, 4, SEEK_SET);

    ret = (*method)(context, client, opcode, req_sp, resp_sp);

out:
    if (req_sp != NULL) {
	krb5_storage_free(req_sp);
    }

    krb5_storage_seek(resp_sp, 0, SEEK_SET);
    krb5_store_int32(resp_sp, ret);

    ret = krb5_storage_to_data(resp_sp, resp_data);
    krb5_storage_free(resp_sp);

    return ret;
}
Example #6
0
kadm5_ret_t
kadm5_c_get_principals(void *server_handle,
		       const char *expression,
		       char ***princs,
		       int *count)
{
    kadm5_client_context *context = server_handle;
    kadm5_ret_t ret;
    krb5_storage *sp;
    unsigned char buf[1024];
    int32_t tmp;
    krb5_data reply;

    ret = _kadm5_connect(server_handle);
    if(ret)
	return ret;

    sp = krb5_storage_from_mem(buf, sizeof(buf));
    if (sp == NULL)
	return ENOMEM;
    krb5_store_int32(sp, kadm_get_princs);
    krb5_store_int32(sp, expression != NULL);
    if(expression)
	krb5_store_string(sp, expression);
    ret = _kadm5_client_send(context, sp);
    krb5_storage_free(sp);
    if (ret)
	return ret;
    ret = _kadm5_client_recv(context, &reply);
    if(ret)
	return ret;
    sp = krb5_storage_from_data (&reply);
    if (sp == NULL) {
	krb5_data_free (&reply);
	return ENOMEM;
    }
    krb5_ret_int32(sp, &tmp);
    ret = tmp;
    if(ret == 0) {
	int i;
	krb5_ret_int32(sp, &tmp);
	*princs = calloc(tmp + 1, sizeof(**princs));
	if (*princs == NULL) {
	    ret = ENOMEM;
	    goto out;
	}
	for(i = 0; i < tmp; i++)
	    krb5_ret_string(sp, &(*princs)[i]);
	*count = tmp;
    }
out:
    krb5_storage_free(sp);
    krb5_data_free (&reply);
    return ret;
}
Example #7
0
kadm5_ret_t
kadm5_c_chpass_principal(void *server_handle,
			 krb5_principal princ,
			 int keepold,
			 int n_ks_tuple,
			 krb5_key_salt_tuple *ks_tuple,
			 const char *password)
{
    kadm5_client_context *context = server_handle;
    kadm5_ret_t ret;
    krb5_storage *sp;
    unsigned char buf[1024];
    int32_t tmp;
    krb5_data reply;

    /*
     * We should get around to implementing this...  At the moment, the
     * the server side API is implemented but the wire protocol has not
     * been updated.
     */
    if (n_ks_tuple > 0)
       return KADM5_KS_TUPLE_NOSUPP;

    ret = _kadm5_connect(server_handle);
    if(ret)
	return ret;

    sp = krb5_storage_from_mem(buf, sizeof(buf));
    if (sp == NULL) {
	krb5_clear_error_message(context->context);
	return ENOMEM;
    }
    krb5_store_int32(sp, kadm_chpass);
    krb5_store_principal(sp, princ);
    krb5_store_string(sp, password);
    krb5_store_int32(sp, keepold); /* extension */
    ret = _kadm5_client_send(context, sp);
    krb5_storage_free(sp);
    if (ret)
	return ret;
    ret = _kadm5_client_recv(context, &reply);
    if(ret)
	return ret;
    sp = krb5_storage_from_data (&reply);
    if (sp == NULL) {
	krb5_clear_error_message(context->context);
	krb5_data_free (&reply);
	return ENOMEM;
    }
    krb5_ret_int32(sp, &tmp);
    krb5_clear_error_message(context->context);
    krb5_storage_free(sp);
    krb5_data_free (&reply);
    return tmp;
}
Example #8
0
kadm5_ret_t
kadm5_c_get_privs(void *server_handle, uint32_t *privs)
{
    kadm5_client_context *context = server_handle;
    kadm5_ret_t ret;
    krb5_storage *sp;
    unsigned char buf[1024];
    int32_t tmp;
    krb5_data reply;

    *privs = 0;

    ret = _kadm5_connect(server_handle);
    if (ret)
	return ret;

    krb5_data_zero(&reply);

    sp = krb5_storage_from_mem(buf, sizeof(buf));
    if (sp == NULL) {
	ret = krb5_enomem(context->context);
	goto out_keep_error;
    }
    ret = krb5_store_int32(sp, kadm_get_privs);
    if (ret)
	goto out;
    ret = _kadm5_client_send(context, sp);
    if (ret)
	goto out_keep_error;
    ret = _kadm5_client_recv(context, &reply);
    if (ret)
	goto out_keep_error;
    krb5_storage_free(sp);
    sp = krb5_storage_from_data(&reply);
    if (sp == NULL) {
	ret = krb5_enomem(context->context);
	goto out_keep_error;
    }
    ret = krb5_ret_int32(sp, &tmp);
    if (ret == 0)
	ret = tmp;
    if (ret == 0)
	krb5_ret_uint32(sp, privs);

  out:
    krb5_clear_error_message(context->context);

  out_keep_error:
    krb5_storage_free(sp);
    krb5_data_free (&reply);
    return ret;
}
Example #9
0
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kcm_call(krb5_context context,
	      krb5_storage *request,
	      krb5_storage **response_p,
	      krb5_data *response_data_p)
{
    krb5_data response_data;
    krb5_error_code ret;
    int32_t status;
    krb5_storage *response;

    if (response_p != NULL)
	*response_p = NULL;

    krb5_data_zero(&response_data);

    ret = kcm_send_request(context, request, &response_data);
    if (ret)
	return ret;

    response = krb5_storage_from_data(&response_data);
    if (response == NULL) {
	krb5_data_free(&response_data);
	return KRB5_CC_IO;
    }

    ret = krb5_ret_int32(response, &status);
    if (ret) {
	krb5_storage_free(response);
	krb5_data_free(&response_data);
	return KRB5_CC_FORMAT;
    }

    if (status) {
	krb5_storage_free(response);
	krb5_data_free(&response_data);
	return status;
    }

    if (response_p != NULL) {
	*response_data_p = response_data;
	*response_p = response;

	return 0;
    }

    krb5_storage_free(response);
    krb5_data_free(&response_data);

    return 0;
}
Example #10
0
static krb5_error_code
kcm_call(krb5_context context,
	 krb5_kcmcache *k,
	 krb5_storage *request,
	 krb5_storage **response_p,
	 krb5_data *response_data_p)
{
    krb5_data response_data;
    krb5_error_code ret, status;
    krb5_storage *response;

    if (response_p != NULL)
	*response_p = NULL;

    ret = kcm_send_request(context, k, request, &response_data);
    if (ret) {
	return ret;
    }

    response = krb5_storage_from_data(&response_data);
    if (response == NULL) {
	krb5_data_free(&response_data);
	return KRB5_CC_IO;
    }

    ret = krb5_ret_int32(response, &status);
    if (ret) {
	krb5_storage_free(response);
	krb5_data_free(&response_data);
	return KRB5_CC_FORMAT;
    }

    if (status) {
	krb5_storage_free(response);
	krb5_data_free(&response_data);
	return status;
    }

    if (response_p != NULL) {
	*response_data_p = response_data;
	*response_p = response;

	return 0;
    }

    krb5_storage_free(response);
    krb5_data_free(&response_data);

    return 0;
}
Example #11
0
kadm5_ret_t
kadm5_c_chpass_principal_with_key(void *server_handle,
				  krb5_principal princ,
				  int keepold,
				  int n_key_data,
				  krb5_key_data *key_data)
{
    kadm5_client_context *context = server_handle;
    kadm5_ret_t ret;
    krb5_storage *sp;
    unsigned char buf[1024];
    int32_t tmp;
    krb5_data reply;
    int i;

    ret = _kadm5_connect(server_handle);
    if(ret)
	return ret;

    sp = krb5_storage_from_mem(buf, sizeof(buf));
    if (sp == NULL) {
	krb5_clear_error_message(context->context);
	return ENOMEM;
    }
    krb5_store_int32(sp, kadm_chpass_with_key);
    krb5_store_principal(sp, princ);
    krb5_store_int32(sp, n_key_data);
    for (i = 0; i < n_key_data; ++i)
	kadm5_store_key_data (sp, &key_data[i]);
    krb5_store_int32(sp, keepold); /* extension */
    ret = _kadm5_client_send(context, sp);
    krb5_storage_free(sp);
    if (ret)
	return ret;
    ret = _kadm5_client_recv(context, &reply);
    if(ret)
	return ret;
    sp = krb5_storage_from_data (&reply);
    if (sp == NULL) {
	krb5_clear_error_message(context->context);
	krb5_data_free (&reply);
	return ENOMEM;
    }
    krb5_ret_int32(sp, &tmp);
    krb5_clear_error_message(context->context);
    krb5_storage_free(sp);
    krb5_data_free (&reply);
    return tmp;
}
Example #12
0
static int
addrport_print_addr (const krb5_address *addr, char *str, size_t len)
{
    krb5_error_code ret;
    krb5_address addr1, addr2;
    uint16_t port = 0;
    size_t ret_len = 0, l, size = 0;
    krb5_storage *sp;

    sp = krb5_storage_from_data((krb5_data*)rk_UNCONST(&addr->address));
    if (sp == NULL)
        return ENOMEM;

    /* for totally obscure reasons, these are not in network byteorder */
    krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);

    krb5_storage_seek(sp, 2, SEEK_CUR); /* skip first two bytes */
    krb5_ret_address(sp, &addr1);

    krb5_storage_seek(sp, 2, SEEK_CUR); /* skip two bytes */
    krb5_ret_address(sp, &addr2);
    krb5_storage_free(sp);
    if(addr2.addr_type == KRB5_ADDRESS_IPPORT && addr2.address.length == 2) {
	unsigned long value;
	_krb5_get_int(addr2.address.data, &value, 2);
	port = value;
    }
    l = strlcpy(str, "ADDRPORT:", len);
    ret_len += l;
    if (len > l)
	size += l;
    else
	size = len;

    ret = krb5_print_address(&addr1, str + size, len - size, &l);
    if (ret)
	return ret;
    ret_len += l;
    if (len - size > l)
	size += l;
    else
	size = len;

    ret = snprintf(str + size, len - size, ",PORT=%u", port);
    if (ret < 0)
	return EINVAL;
    ret_len += ret;
    return ret_len;
}
Example #13
0
kadm5_ret_t
kadm5_c_get_principal(void *server_handle,
		      krb5_principal princ,
		      kadm5_principal_ent_t out,
		      uint32_t mask)
{
    kadm5_client_context *context = server_handle;
    kadm5_ret_t ret;
    krb5_storage *sp;
    unsigned char buf[1024];
    int32_t tmp;
    krb5_data reply;

    ret = _kadm5_connect(server_handle);
    if(ret)
	return ret;

    sp = krb5_storage_from_mem(buf, sizeof(buf));
    if (sp == NULL) {
	krb5_clear_error_message(context->context);
	return ENOMEM;
    }
    krb5_store_int32(sp, kadm_get);
    krb5_store_principal(sp, princ);
    krb5_store_int32(sp, mask);
    ret = _kadm5_client_send(context, sp);
    krb5_storage_free(sp);
    if(ret)
	return ret;
    ret = _kadm5_client_recv(context, &reply);
    if (ret)
	return ret;
    sp = krb5_storage_from_data (&reply);
    if (sp == NULL) {
	krb5_clear_error_message(context->context);
	krb5_data_free (&reply);
	return ENOMEM;
    }
    krb5_ret_int32(sp, &tmp);
    ret = tmp;
    krb5_clear_error_message(context->context);
    if(ret == 0)
	kadm5_ret_principal_ent(sp, out);
    krb5_storage_free(sp);
    krb5_data_free (&reply);
    return ret;
}
Example #14
0
krb5_error_code
_kadm5_xdr_ret_gacred(krb5_data *data, struct _kadm5_xdr_gacred *gacred)
{
    krb5_storage *sp;

    memset(gacred, 0, sizeof(*gacred));

    sp = krb5_storage_from_data(data);
    INSIST(sp != NULL);

    CHECK(krb5_ret_uint32(sp, &gacred->version));
    CHECK(krb5_ret_uint32(sp, &gacred->auth_msg));
    CHECK(_kadm5_xdr_ret_data_xdr(sp, &gacred->handle));

    krb5_storage_free(sp);

    return 0;
}
Example #15
0
kadm5_ret_t
kadm5_c_chpass_principal(void *server_handle,
                         krb5_principal princ,
                         const char *password)
{
    kadm5_client_context *context = server_handle;
    kadm5_ret_t ret;
    krb5_storage *sp;
    unsigned char buf[1024];
    int32_t tmp;
    krb5_data reply;

    ret = _kadm5_connect(server_handle);
    if(ret)
        return ret;

    sp = krb5_storage_from_mem(buf, sizeof(buf));
    if (sp == NULL) {
        krb5_clear_error_message(context->context);
        return ENOMEM;
    }
    krb5_store_int32(sp, kadm_chpass);
    krb5_store_principal(sp, princ);
    krb5_store_string(sp, password);
    ret = _kadm5_client_send(context, sp);
    krb5_storage_free(sp);
    if (ret)
        return ret;
    ret = _kadm5_client_recv(context, &reply);
    if(ret)
        return ret;
    sp = krb5_storage_from_data (&reply);
    if (sp == NULL) {
        krb5_clear_error_message(context->context);
        krb5_data_free (&reply);
        return ENOMEM;
    }
    krb5_ret_int32(sp, &tmp);
    krb5_clear_error_message(context->context);
    krb5_storage_free(sp);
    krb5_data_free (&reply);
    return tmp;
}
Example #16
0
kadm5_ret_t
kadm5_c_rename_principal(void *server_handle,
			 krb5_principal source,
			 krb5_principal target)
{
    kadm5_client_context *context = server_handle;
    kadm5_ret_t ret;
    krb5_storage *sp;
    unsigned char buf[1024];
    int32_t tmp;
    krb5_data reply;

    ret = _kadm5_connect(server_handle);
    if(ret)
	return ret;

    sp = krb5_storage_from_mem(buf, sizeof(buf));
    if (sp == NULL)
	return ENOMEM;
    krb5_store_int32(sp, kadm_rename);
    krb5_store_principal(sp, source);
    krb5_store_principal(sp, target);
    ret = _kadm5_client_send(context, sp);
    krb5_storage_free(sp);
    if (ret)
	return ret;
    ret = _kadm5_client_recv(context, &reply);
    if(ret)
	return ret;
    sp = krb5_storage_from_data (&reply);
    if (sp == NULL) {
	krb5_data_free (&reply);
	return ENOMEM;
    }
    krb5_ret_int32(sp, &tmp);
    ret = tmp;
    krb5_storage_free(sp);
    krb5_data_free (&reply);
    return ret;
}
Example #17
0
static void
send_im_here (krb5_context context, int fd,
	      krb5_auth_context auth_context)
{
    krb5_storage *sp;
    krb5_data data;
    int ret;

    ret = krb5_data_alloc (&data, 4);
    if (ret)
	krb5_err (context, 1, ret, "send_im_here");

    sp = krb5_storage_from_data (&data);
    if (sp == NULL)
	krb5_errx (context, 1, "krb5_storage_from_data");
    krb5_store_int32(sp, I_AM_HERE);
    krb5_storage_free(sp);

    ret = krb5_write_priv_message(context, auth_context, &fd, &data);
    krb5_data_free(&data);

    if (ret)
	krb5_err (context, 1, ret, "krb5_write_priv_message");
}
Example #18
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 #19
0
static krb5_error_code
mdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry *entry)
{
    krb5_error_code ret;
    krb5_storage *sp;
    uint32_t u32;
    uint16_t u16, num_keys, num_tl;
    size_t i, j;
    char *p;

    sp = krb5_storage_from_data(data);
    if (sp == NULL) {
        krb5_set_error_message(context, ENOMEM, "out of memory");
        return ENOMEM;
    }

    krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);

    /*
     * 16: baselength
     *
     * The story here is that these 16 bits have to be a constant:
     * KDB_V1_BASE_LENGTH.  Once upon a time a different value here
     * would have been used to indicate the presence of "extra data"
     * between the "base" contents and the {principal name, TL data,
     * keys} that follow it.  Nothing supports such "extra data"
     * nowadays, so neither do we here.
     *
     * XXX But... surely we ought to log about this extra data, or skip
     * it, or something, in case anyone has MIT KDBs with ancient
     * entries in them...  Logging would allow the admin to know which
     * entries to dump with MIT krb5's kdb5_util.
     */
    CHECK(ret = krb5_ret_uint16(sp, &u16));
    if (u16 != KDB_V1_BASE_LENGTH) {
        ret = EINVAL;
        goto out;
    }
    /* 32: attributes */
    CHECK(ret = krb5_ret_uint32(sp, &u32));
    entry->flags.postdate =	 !(u32 & KRB5_KDB_DISALLOW_POSTDATED);
    entry->flags.forwardable =	 !(u32 & KRB5_KDB_DISALLOW_FORWARDABLE);
    entry->flags.initial =	!!(u32 & KRB5_KDB_DISALLOW_TGT_BASED);
    entry->flags.renewable =	 !(u32 & KRB5_KDB_DISALLOW_RENEWABLE);
    entry->flags.proxiable =	 !(u32 & KRB5_KDB_DISALLOW_PROXIABLE);
    /* DUP_SKEY */
    entry->flags.invalid =	!!(u32 & KRB5_KDB_DISALLOW_ALL_TIX);
    entry->flags.require_preauth =!!(u32 & KRB5_KDB_REQUIRES_PRE_AUTH);
    entry->flags.require_hwauth =!!(u32 & KRB5_KDB_REQUIRES_HW_AUTH);
    entry->flags.server =	 !(u32 & KRB5_KDB_DISALLOW_SVR);
    entry->flags.change_pw = 	!!(u32 & KRB5_KDB_PWCHANGE_SERVICE);
    entry->flags.client =	   1; /* XXX */

    /* 32: max time */
    CHECK(ret = krb5_ret_uint32(sp, &u32));
    if (u32) {
        entry->max_life = malloc(sizeof(*entry->max_life));
        *entry->max_life = u32;
    }
    /* 32: max renewable time */
    CHECK(ret = krb5_ret_uint32(sp, &u32));
    if (u32) {
        entry->max_renew = malloc(sizeof(*entry->max_renew));
        *entry->max_renew = u32;
    }
    /* 32: client expire */
    CHECK(ret = krb5_ret_uint32(sp, &u32));
    if (u32) {
        entry->valid_end = malloc(sizeof(*entry->valid_end));
        *entry->valid_end = u32;
    }
    /* 32: passwd expire */
    CHECK(ret = krb5_ret_uint32(sp, &u32));
    if (u32) {
        entry->pw_end = malloc(sizeof(*entry->pw_end));
        *entry->pw_end = u32;
    }
    /* 32: last successful passwd */
    CHECK(ret = krb5_ret_uint32(sp, &u32));
    /* 32: last failed attempt */
    CHECK(ret = krb5_ret_uint32(sp, &u32));
    /* 32: num of failed attempts */
    CHECK(ret = krb5_ret_uint32(sp, &u32));
    /* 16: num tl data */
    CHECK(ret = krb5_ret_uint16(sp, &u16));
    num_tl = u16;
    /* 16: num key data */
    CHECK(ret = krb5_ret_uint16(sp, &u16));
    num_keys = u16;
    /* 16: principal length */
    CHECK(ret = krb5_ret_uint16(sp, &u16));
    /* length: principal */
    {
        /*
         * Note that the principal name includes the NUL in the entry,
         * but we don't want to take chances, so we add an extra NUL.
         */
        p = malloc(u16 + 1);
        if (p == NULL) {
            ret = ENOMEM;
            goto out;
        }
        krb5_storage_read(sp, p, u16);
        p[u16] = '\0';
        CHECK(ret = krb5_parse_name(context, p, &entry->principal));
        free(p);
    }
    /* for num tl data times
           16: tl data type
           16: tl data length
           length: length */
    for (i = 0; i < num_tl; i++) {
        /* 16: TL data type */
        CHECK(ret = krb5_ret_uint16(sp, &u16));
        /* 16: TL data length */
        CHECK(ret = krb5_ret_uint16(sp, &u16));
        krb5_storage_seek(sp, u16, SEEK_CUR);
    }
    /*
     * for num key data times
     * 16: "version"
     * 16: kvno
     * for version times:
     *     16: type
     *     16: length
     *     length: keydata
     *
     * "version" here is really 1 or 2, the first meaning there's only
     * keys for this kvno, the second meaning there's keys and salt[s?].
     * That's right... hold that gag reflex, you can do it.
     */
    for (i = 0; i < num_keys; i++) {
        int keep = 0;
        uint16_t version;
        void *ptr;

        CHECK(ret = krb5_ret_uint16(sp, &u16));
        version = u16;
        CHECK(ret = krb5_ret_uint16(sp, &u16));

        /*
         * First time through, and until we find one matching key,
         * entry->kvno == 0.
         */
        if ((entry->kvno < u16) && (kvno == 0 || kvno == u16)) {
            keep = 1;
            entry->kvno = u16;
            /*
             * Found a higher kvno than earlier, so free the old highest
             * kvno keys.
             *
             * XXX Of course, we actually want to extract the old kvnos
             * as well, for some of the kadm5 APIs.  We shouldn't free
             * these keys, but keep them elsewhere.
             */
            for (j = 0; j < entry->keys.len; j++)
                free_Key(&entry->keys.val[j]);
            free(entry->keys.val);
            entry->keys.len = 0;
            entry->keys.val = NULL;
        } else if (entry->kvno == u16)
            /* Accumulate keys */
            keep = 1;

        if (keep) {
            Key *k;

            ptr = realloc(entry->keys.val, sizeof(entry->keys.val[0]) * (entry->keys.len + 1));
            if (ptr == NULL) {
                ret = ENOMEM;
                goto out;
            }
            entry->keys.val = ptr;

            /* k points to current Key */
            k = &entry->keys.val[entry->keys.len];

            memset(k, 0, sizeof(*k));
            entry->keys.len += 1;

            k->mkvno = malloc(sizeof(*k->mkvno));
            if (k->mkvno == NULL) {
                ret = ENOMEM;
                goto out;
            }
            *k->mkvno = 1;

            for (j = 0; j < version; j++) {
                uint16_t type;
                CHECK(ret = krb5_ret_uint16(sp, &type));
                CHECK(ret = krb5_ret_uint16(sp, &u16));
                if (j == 0) {
                    /* This "version" means we have a key */
                    k->key.keytype = type;
                    if (u16 < 2) {
                        ret = EINVAL;
                        goto out;
                    }
                    /*
                     * MIT stores keys encrypted keys as {16-bit length
                     * of plaintext key, {encrypted key}}.  The reason
                     * for this is that the Kerberos cryptosystem is not
                     * length-preserving.  Heimdal's approach is to
                     * truncate the plaintext to the expected length of
                     * the key given its enctype, so we ignore this
                     * 16-bit length-of-plaintext-key field.
                     */
                    krb5_storage_seek(sp, 2, SEEK_CUR); /* skip real length */
                    k->key.keyvalue.length = u16 - 2;   /* adjust cipher len */
                    k->key.keyvalue.data = malloc(k->key.keyvalue.length);
                    krb5_storage_read(sp, k->key.keyvalue.data,
                                      k->key.keyvalue.length);
                } else if (j == 1) {
                    /* This "version" means we have a salt */
                    k->salt = calloc(1, sizeof(*k->salt));
                    if (k->salt == NULL) {
                        ret = ENOMEM;
                        goto out;
                    }
                    k->salt->type = type;
                    if (u16 != 0) {
                        k->salt->salt.data = malloc(u16);
                        if (k->salt->salt.data == NULL) {
                            ret = ENOMEM;
                            goto out;
                        }
                        k->salt->salt.length = u16;
                        krb5_storage_read(sp, k->salt->salt.data, k->salt->salt.length);
                    }
                    fix_salt(context, entry, entry->keys.len - 1);
                } else {
                    /*
                     * Whatever this "version" might be, we skip it
                     *
                     * XXX A krb5.conf parameter requesting that we log
                     * about strangeness like this, or return an error
                     * from here, might be nice.
                     */
                    krb5_storage_seek(sp, u16, SEEK_CUR);
                }
            }
        } else {
            /*
             * XXX For now we skip older kvnos, but we should extract
             * them...
             */
            for (j = 0; j < version; j++) {
                /* enctype */
                CHECK(ret = krb5_ret_uint16(sp, &u16));
                /* encrypted key (or plaintext salt) */
                CHECK(ret = krb5_ret_uint16(sp, &u16));
                krb5_storage_seek(sp, u16, SEEK_CUR);
            }
        }
    }

    if (entry->kvno == 0 && kvno != 0) {
        ret = HDB_ERR_NOT_FOUND_HERE;
        goto out;
    }

    return 0;
out:
    if (ret == HEIM_ERR_EOF)
        /* Better error code than "end of file" */
        ret = HEIM_ERR_BAD_HDBENT_ENCODING;
    return ret;
}
Example #20
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;
}
Example #21
0
krb5_error_code KRB5_LIB_FUNCTION
_krb5_krb_rd_req(krb5_context context,
		 krb5_data *authent,
		 const char *service,
		 const char *instance,
		 const char *local_realm,
		 int32_t from_addr,
		 const krb5_keyblock *key,
		 struct _krb5_krb_auth_data *ad)
{
    krb5_error_code ret;
    krb5_storage *sp;
    krb5_data ticket, eaut, aut;
    krb5_ssize_t size;
    int little_endian;
    int8_t pvno;
    int8_t type;
    int8_t s_kvno;
    uint8_t ticket_length;
    uint8_t eaut_length;
    uint8_t time_5ms;
    char *realm = NULL;
    char *sname = NULL;
    char *sinstance = NULL;
    char *r_realm = NULL;
    char *r_name = NULL;
    char *r_instance = NULL;

    uint32_t r_time_sec;	/* Coarse time from authenticator */
    unsigned long delta_t;      /* Time in authenticator - local time */
    long tkt_age;		/* Age of ticket */

    struct timeval tv;

    krb5_data_zero(&ticket);
    krb5_data_zero(&eaut);
    krb5_data_zero(&aut);

    sp = krb5_storage_from_data(authent);
    if (sp == NULL) {
	krb5_set_error_string(context, "alloc: out of memory");
	return ENOMEM;
    }

    krb5_storage_set_eof_code(sp, EINVAL); /* XXX */

    ret = krb5_ret_int8(sp, &pvno);
    if (ret)
	goto error;

    if (pvno != KRB_PROT_VERSION) {
	ret = EINVAL; /* XXX */
	goto error;
    }

    ret = krb5_ret_int8(sp, &type);
    if (ret)
	goto error;

    little_endian = type & 1;
    type &= ~1;
    
    if(type != AUTH_MSG_APPL_REQUEST && type != AUTH_MSG_APPL_REQUEST_MUTUAL) {
	ret = EINVAL; /* RD_AP_MSG_TYPE */
	goto error;
    }

    RCHECK(ret, krb5_ret_int8(sp, &s_kvno), error);
    RCHECK(ret, get_v4_stringz(sp, &realm, REALM_SZ), error);
    RCHECK(ret, krb5_ret_uint8(sp, &ticket_length), error);
    RCHECK(ret, krb5_ret_uint8(sp, &eaut_length), error);
    RCHECK(ret, krb5_data_alloc(&ticket, ticket_length), error);

    size = krb5_storage_read(sp, ticket.data, ticket.length);
    if (size != ticket.length) {
	ret = EINVAL;
	goto error;
    }

    /* Decrypt and take apart ticket */
    ret = _krb5_krb_decomp_ticket(context, &ticket, key, local_realm, 
				  &sname, &sinstance, ad);
    if (ret)
	goto error;

    RCHECK(ret, krb5_data_alloc(&eaut, eaut_length), error);

    size = krb5_storage_read(sp, eaut.data, eaut.length);
    if (size != eaut.length) {
	ret = EINVAL;
	goto error;
    }

    krb5_storage_free(sp);
    sp = NULL;

    ret = decrypt_etext(context, &ad->session, &eaut, &aut);
    if (ret)
	goto error;

    sp = krb5_storage_from_data(&aut);
    if (sp == NULL) {
	krb5_set_error_string(context, "alloc: out of memory");
	ret = ENOMEM;
	goto error;
    }

    if (little_endian)
	krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
    else
	krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);

    RCHECK(ret, get_v4_stringz(sp, &r_name, ANAME_SZ), error);
    RCHECK(ret, get_v4_stringz(sp, &r_instance, INST_SZ), error);
    RCHECK(ret, get_v4_stringz(sp, &r_realm, REALM_SZ), error);

    RCHECK(ret, krb5_ret_uint32(sp, &ad->checksum), error);
    RCHECK(ret, krb5_ret_uint8(sp, &time_5ms), error);
    RCHECK(ret, krb5_ret_uint32(sp, &r_time_sec), error);

    if (strcmp(ad->pname, r_name) != 0 ||
	strcmp(ad->pinst, r_instance) != 0 ||
	strcmp(ad->prealm, r_realm) != 0) {
	ret = EINVAL; /* RD_AP_INCON */
	goto error;
    }
    
    if (from_addr && from_addr != ad->address) {
	ret = EINVAL; /* RD_AP_BADD */
	goto error;
    }

    gettimeofday(&tv, NULL);
    delta_t = abs((int)(tv.tv_sec - r_time_sec));
    if (delta_t > CLOCK_SKEW) {
        ret = EINVAL; /* RD_AP_TIME */
	goto error;
    }

    /* Now check for expiration of ticket */

    tkt_age = tv.tv_sec - ad->time_sec;
    
    if ((tkt_age < 0) && (-tkt_age > CLOCK_SKEW)) {
        ret = EINVAL; /* RD_AP_NYV */
	goto error;
    }

    if (tv.tv_sec > _krb5_krb_life_to_time(ad->time_sec, ad->life)) {
	ret = EINVAL; /* RD_AP_EXP */
	goto error;
    }

    ret = 0;
 error:
    krb5_data_free(&ticket);
    krb5_data_free(&eaut);
    krb5_data_free(&aut);
    if (realm)
	free(realm);
    if (sname)
	free(sname);
    if (sinstance)
	free(sinstance);
    if (r_name)
	free(r_name);
    if (r_instance)
	free(r_instance);
    if (r_realm)
	free(r_realm);
    if (sp)
	krb5_storage_free(sp);

    if (ret)
	krb5_clear_error_string(context);

    return ret;
}
Example #22
0
krb5_error_code KRB5_LIB_FUNCTION
_krb5_krb_decomp_ticket(krb5_context context,
			const krb5_data *enc_ticket,
			const krb5_keyblock *key,
			const char *local_realm,
			char **sname,
			char **sinstance,
			struct _krb5_krb_auth_data *ad)
{
    krb5_error_code ret;
    krb5_ssize_t size;
    krb5_storage *sp = NULL;
    krb5_data ticket;
    unsigned char des_key[8];

    memset(ad, 0, sizeof(*ad));
    krb5_data_zero(&ticket);

    *sname = NULL;
    *sinstance = NULL;

    RCHECK(ret, decrypt_etext(context, key, enc_ticket, &ticket), error);

    sp = krb5_storage_from_data(&ticket);
    if (sp == NULL) {
	krb5_data_free(&ticket);
	krb5_set_error_string(context, "alloc: out of memory");
	return ENOMEM;
    }

    krb5_storage_set_eof_code(sp, EINVAL); /* XXX */

    RCHECK(ret, krb5_ret_int8(sp, &ad->k_flags), error);
    RCHECK(ret, get_v4_stringz(sp, &ad->pname, ANAME_SZ), error);
    RCHECK(ret, get_v4_stringz(sp, &ad->pinst, INST_SZ), error);
    RCHECK(ret, get_v4_stringz(sp, &ad->prealm, REALM_SZ), error);
    RCHECK(ret, krb5_ret_uint32(sp, &ad->address), error);
	
    size = krb5_storage_read(sp, des_key, sizeof(des_key));
    if (size != sizeof(des_key)) {
	ret = EINVAL; /* XXX */
	goto error;
    }

    RCHECK(ret, krb5_ret_uint8(sp, &ad->life), error);

    if (ad->k_flags & 1)
	krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
    else
	krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);

    RCHECK(ret, krb5_ret_uint32(sp, &ad->time_sec), error);

    RCHECK(ret, get_v4_stringz(sp, sname, ANAME_SZ), error);
    RCHECK(ret, get_v4_stringz(sp, sinstance, INST_SZ), error);

    ret = krb5_keyblock_init(context, ETYPE_DES_PCBC_NONE,
			     des_key, sizeof(des_key), &ad->session);
    if (ret)
	goto error;

    if (strlen(ad->prealm) == 0) {
	free(ad->prealm);
	ad->prealm = strdup(local_realm);
	if (ad->prealm == NULL) {
	    ret = ENOMEM;
	    goto error;
	}
    }

 error:
    memset(des_key, 0, sizeof(des_key));
    if (sp)
	krb5_storage_free(sp);
    krb5_data_free(&ticket);
    if (ret) {
	if (*sname) {
	    free(*sname);
	    *sname = NULL;
	}
	if (*sinstance) {
	    free(*sinstance);
	    *sinstance = NULL;
	}
	_krb5_krb_free_auth_data(context, ad);
	krb5_set_error_string(context, "Failed to decode v4 ticket");
    }
    return ret;
}
Example #23
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 #24
0
static kadm5_ret_t
kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
		 krb5_data *in, krb5_data *out)
{
    kadm5_ret_t ret;
    int32_t cmd, mask, tmp;
    kadm5_server_context *contextp = kadm_handlep;
    char client[128], name[128], name2[128];
    const char *op = "";
    krb5_principal princ, princ2;
    kadm5_principal_ent_rec ent;
    char *password, *expression;
    krb5_keyblock *new_keys;
    krb5_key_salt_tuple *ks_tuple = NULL;
    krb5_boolean keepold = FALSE;
    int n_ks_tuple = 0;
    int n_keys;
    char **princs;
    int n_princs;
    int keys_ok = 0;
    krb5_storage *sp;

    krb5_unparse_name_fixed(contextp->context, contextp->caller,
			    client, sizeof(client));

    sp = krb5_storage_from_data(in);
    if (sp == NULL)
	krb5_errx(contextp->context, 1, "out of memory");

    krb5_ret_int32(sp, &cmd);
    switch(cmd){
    case kadm_get:{
	op = "GET";
	ret = krb5_ret_principal(sp, &princ);
	if(ret)
	    goto fail;
	ret = krb5_ret_int32(sp, &mask);
	if(ret){
	    krb5_free_principal(contextp->context, princ);
	    goto fail;
	}
	mask |= KADM5_PRINCIPAL;
	krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);

        /* If the caller doesn't have KADM5_PRIV_GET, we're done. */
	ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET, princ);
        if (ret) {
	    krb5_free_principal(contextp->context, princ);
	    goto fail;
        }

        /* Then check to see if it is ok to return keys */
        if ((mask & KADM5_KEY_DATA) != 0) {
            ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET_KEYS,
                                              princ);
            if (ret == 0) {
                keys_ok = 1;
            } else if ((mask == (KADM5_PRINCIPAL|KADM5_KEY_DATA)) ||
                       (mask == (KADM5_PRINCIPAL|KADM5_KVNO|KADM5_KEY_DATA))) {
                /*
                 * Requests for keys will get bogus keys, which is useful if
                 * the client just wants to see what (kvno, enctype)s the
                 * principal has keys for, but terrible if the client wants to
                 * write the keys into a keytab or modify the principal and
                 * write the bogus keys back to the server.
                 *
                 * We use a heuristic to detect which case we're handling here.
                 * If the client only asks for the flags in the above
                 * condition, then it's very likely a kadmin ext_keytab,
                 * add_enctype, or other request that should not see bogus
                 * keys.  We deny them.
                 *
                 * The kadmin get command can be coaxed into making a request
                 * with the same mask.  But the default long and terse output
                 * modes request other things too, so in all likelihood this
                 * heuristic will not hurt any kadmin get uses.
                 */
                krb5_free_principal(contextp->context, princ);
                goto fail;
            }
        }

	ret = kadm5_get_principal(kadm_handlep, princ, &ent, mask);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	if (ret == 0){
	    if (keys_ok)
		kadm5_store_principal_ent(sp, &ent);
	    else
		kadm5_store_principal_ent_nokeys(sp, &ent);
	    kadm5_free_principal_ent(kadm_handlep, &ent);
	}
	krb5_free_principal(contextp->context, princ);
	break;
    }
    case kadm_delete:{
	op = "DELETE";
	ret = krb5_ret_principal(sp, &princ);
	if(ret)
	    goto fail;
	krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
	ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_DELETE, princ);
	if(ret){
	    krb5_free_principal(contextp->context, princ);
	    goto fail;
	}
	ret = kadm5_delete_principal(kadm_handlep, princ);
	krb5_free_principal(contextp->context, princ);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	break;
    }
    case kadm_create:{
	op = "CREATE";
	ret = kadm5_ret_principal_ent(sp, &ent);
	if(ret)
	    goto fail;
	ret = krb5_ret_int32(sp, &mask);
	if(ret){
	    kadm5_free_principal_ent(contextp->context, &ent);
	    goto fail;
	}
	ret = krb5_ret_string(sp, &password);
	if(ret){
	    kadm5_free_principal_ent(contextp->context, &ent);
	    goto fail;
	}
	krb5_unparse_name_fixed(contextp->context, ent.principal,
				name, sizeof(name));
	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
	ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_ADD,
					  ent.principal);
	if(ret){
	    kadm5_free_principal_ent(contextp->context, &ent);
	    memset(password, 0, strlen(password));
	    free(password);
	    goto fail;
	}
	ret = kadm5_create_principal(kadm_handlep, &ent,
				     mask, password);
	kadm5_free_principal_ent(kadm_handlep, &ent);
	memset(password, 0, strlen(password));
	free(password);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	break;
    }
    case kadm_modify:{
	op = "MODIFY";
	ret = kadm5_ret_principal_ent(sp, &ent);
	if(ret)
	    goto fail;
	ret = krb5_ret_int32(sp, &mask);
	if(ret){
	    kadm5_free_principal_ent(contextp, &ent);
	    goto fail;
	}
	krb5_unparse_name_fixed(contextp->context, ent.principal,
				name, sizeof(name));
	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
	ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_MODIFY,
					  ent.principal);
	if(ret){
	    kadm5_free_principal_ent(contextp, &ent);
	    goto fail;
	}
	ret = kadm5_modify_principal(kadm_handlep, &ent, mask);
	kadm5_free_principal_ent(kadm_handlep, &ent);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	break;
    }
    case kadm_rename:{
	op = "RENAME";
	ret = krb5_ret_principal(sp, &princ);
	if(ret)
	    goto fail;
	ret = krb5_ret_principal(sp, &princ2);
	if(ret){
	    krb5_free_principal(contextp->context, princ);
	    goto fail;
	}
	krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
	krb5_unparse_name_fixed(contextp->context, princ2, name2, sizeof(name2));
	krb5_warnx(contextp->context, "%s: %s %s -> %s",
		   client, op, name, name2);
	ret = _kadm5_acl_check_permission(contextp,
					  KADM5_PRIV_ADD,
					  princ2)
	    || _kadm5_acl_check_permission(contextp,
					   KADM5_PRIV_DELETE,
					   princ);
	if(ret){
	    krb5_free_principal(contextp->context, princ);
	    krb5_free_principal(contextp->context, princ2);
	    goto fail;
	}
	ret = kadm5_rename_principal(kadm_handlep, princ, princ2);
	krb5_free_principal(contextp->context, princ);
	krb5_free_principal(contextp->context, princ2);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	break;
    }
    case kadm_chpass:{
	op = "CHPASS";
	ret = krb5_ret_principal(sp, &princ);
	if (ret)
	    goto fail;
	ret = krb5_ret_string(sp, &password);
	if (ret) {
	    krb5_free_principal(contextp->context, princ);
	    goto fail;
	}
	ret = krb5_ret_int32(sp, &keepold);
	if (ret && ret != HEIM_ERR_EOF) {
	    krb5_free_principal(contextp->context, princ);
	    goto fail;
	}
	krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);

	/*
	 * The change is allowed if at least one of:
	 *
	 * a) allowed by sysadmin
	 * b) it's for the principal him/herself and this was an
	 *    initial ticket, but then, check with the password quality
	 *    function.
	 * c) the user is on the CPW ACL.
	 */

	if (krb5_config_get_bool_default(contextp->context, NULL, TRUE,
					 "kadmin", "allow_self_change_password", NULL)
	    && initial
	    && krb5_principal_compare (contextp->context, contextp->caller,
				       princ))
	{
	    krb5_data pwd_data;
	    const char *pwd_reason;

	    pwd_data.data = password;
	    pwd_data.length = strlen(password);

	    pwd_reason = kadm5_check_password_quality (contextp->context,
						       princ, &pwd_data);
	    if (pwd_reason != NULL)
		ret = KADM5_PASS_Q_DICT;
	    else
		ret = 0;
	} else
	    ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);

	if(ret) {
	    krb5_free_principal(contextp->context, princ);
	    memset(password, 0, strlen(password));
	    free(password);
	    goto fail;
	}
	ret = kadm5_chpass_principal_3(kadm_handlep, princ, keepold, 0, NULL,
				       password);
	krb5_free_principal(contextp->context, princ);
	memset(password, 0, strlen(password));
	free(password);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	break;
    }
    case kadm_chpass_with_key:{
	int i;
	krb5_key_data *key_data;
	int n_key_data;

	op = "CHPASS_WITH_KEY";
	ret = krb5_ret_principal(sp, &princ);
	if(ret)
	    goto fail;
	ret = krb5_ret_int32(sp, &n_key_data);
	if (ret) {
	    krb5_free_principal(contextp->context, princ);
	    goto fail;
	}
	ret = krb5_ret_int32(sp, &keepold);
	if (ret && ret != HEIM_ERR_EOF) {
	    krb5_free_principal(contextp->context, princ);
	    goto fail;
	}
	/* n_key_data will be squeezed into an int16_t below. */
	if (n_key_data < 0 || n_key_data >= 1 << 16 ||
	    (size_t)n_key_data > UINT_MAX/sizeof(*key_data)) {
	    ret = ERANGE;
	    krb5_free_principal(contextp->context, princ);
	    goto fail;
	}

	key_data = malloc (n_key_data * sizeof(*key_data));
	if (key_data == NULL && n_key_data != 0) {
	    ret = ENOMEM;
	    krb5_free_principal(contextp->context, princ);
	    goto fail;
	}

	for (i = 0; i < n_key_data; ++i) {
	    ret = kadm5_ret_key_data (sp, &key_data[i]);
	    if (ret) {
		int16_t dummy = i;

		kadm5_free_key_data (contextp, &dummy, key_data);
		free (key_data);
		krb5_free_principal(contextp->context, princ);
		goto fail;
	    }
	}

	krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);

	/*
	 * The change is only allowed if the user is on the CPW ACL,
	 * this it to force password quality check on the user.
	 */

	ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
	if(ret) {
	    int16_t dummy = n_key_data;

	    kadm5_free_key_data (contextp, &dummy, key_data);
	    free (key_data);
	    krb5_free_principal(contextp->context, princ);
	    goto fail;
	}
	ret = kadm5_chpass_principal_with_key_3(kadm_handlep, princ, keepold,
					        n_key_data, key_data);
	{
	    int16_t dummy = n_key_data;
	    kadm5_free_key_data (contextp, &dummy, key_data);
	}
	free (key_data);
	krb5_free_principal(contextp->context, princ);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	break;
    }
    case kadm_randkey:{
	op = "RANDKEY";
	ret = krb5_ret_principal(sp, &princ);
	if(ret)
	    goto fail;
	krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
	/*
	 * The change is allowed if at least one of:
	 * a) it's for the principal him/herself and this was an initial ticket
	 * b) the user is on the CPW ACL.
	 */

	if (initial
	    && krb5_principal_compare (contextp->context, contextp->caller,
				       princ))
	    ret = 0;
	else
	    ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);

	if(ret) {
	    krb5_free_principal(contextp->context, princ);
	    goto fail;
	}

	/*
	 * See comments in kadm5_c_randkey_principal() regarding the
	 * protocol.
	 */
	ret = krb5_ret_int32(sp, &keepold);
	if (ret != 0 && ret != HEIM_ERR_EOF) {
	    krb5_free_principal(contextp->context, princ);
	    goto fail;
	}

	ret = krb5_ret_int32(sp, &n_ks_tuple);
	if (ret != 0 && ret != HEIM_ERR_EOF) {
	    krb5_free_principal(contextp->context, princ);
	    goto fail;
	} else if (ret == 0) {
	    size_t i;

	    if (n_ks_tuple < 0) {
		ret = EOVERFLOW;
		krb5_free_principal(contextp->context, princ);
		goto fail;
	    }

	    if ((ks_tuple = calloc(n_ks_tuple, sizeof (*ks_tuple))) == NULL) {
		ret = errno;
		krb5_free_principal(contextp->context, princ);
		goto fail;
	    }

	    for (i = 0; i < n_ks_tuple; i++) {
		ret = krb5_ret_int32(sp, &ks_tuple[i].ks_enctype);
		if (ret != 0) {
		    krb5_free_principal(contextp->context, princ);
		    goto fail;
		}
		ret = krb5_ret_int32(sp, &ks_tuple[i].ks_salttype);
		if (ret != 0) {
		    krb5_free_principal(contextp->context, princ);
		    goto fail;
		}
	    }
	}
	ret = kadm5_randkey_principal_3(kadm_handlep, princ, keepold,
					n_ks_tuple, ks_tuple, &new_keys,
					&n_keys);
	krb5_free_principal(contextp->context, princ);

	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	if(ret == 0){
	    int i;
	    krb5_store_int32(sp, n_keys);
	    for(i = 0; i < n_keys; i++){
		krb5_store_keyblock(sp, new_keys[i]);
		krb5_free_keyblock_contents(contextp->context, &new_keys[i]);
	    }
	    free(new_keys);
	}
	break;
    }
    case kadm_get_privs:{
	uint32_t privs;
	ret = kadm5_get_privs(kadm_handlep, &privs);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	if(ret == 0)
	    krb5_store_uint32(sp, privs);
	break;
    }
    case kadm_get_princs:{
	op = "LIST";
	ret = krb5_ret_int32(sp, &tmp);
	if(ret)
	    goto fail;
	if(tmp){
	    ret = krb5_ret_string(sp, &expression);
	    if(ret)
		goto fail;
	}else
	    expression = NULL;
	krb5_warnx(contextp->context, "%s: %s %s", client, op,
		   expression ? expression : "*");
	ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_LIST, NULL);
	if(ret){
	    free(expression);
	    goto fail;
	}
	ret = kadm5_get_principals(kadm_handlep, expression, &princs, &n_princs);
	free(expression);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	if(ret == 0){
	    int i;
	    krb5_store_int32(sp, n_princs);
	    for(i = 0; i < n_princs; i++)
		krb5_store_string(sp, princs[i]);
	    kadm5_free_name_list(kadm_handlep, princs, &n_princs);
	}
	break;
    }
    default:
	krb5_warnx(contextp->context, "%s: UNKNOWN OP %d", client, cmd);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, KADM5_FAILURE);
	break;
    }
    krb5_storage_to_data(sp, out);
    krb5_storage_free(sp);
    return 0;
fail:
    krb5_warn(contextp->context, ret, "%s", op);
    krb5_storage_seek(sp, 0, SEEK_SET);
    krb5_store_int32(sp, ret);
    krb5_storage_to_data(sp, out);
    krb5_storage_free(sp);
    return 0;
}
Example #25
0
static kadm5_ret_t
kadmind_dispatch(void *kadm_handle, krb5_boolean initial,
		 krb5_data *in, krb5_data *out)
{
    kadm5_ret_t ret;
    int32_t cmd, mask, tmp;
    kadm5_server_context *context = kadm_handle;
    char client[128], name[128], name2[128];
    char *op = "";
    krb5_principal princ, princ2;
    kadm5_principal_ent_rec ent;
    char *password, *expression;
    krb5_keyblock *new_keys;
    int n_keys;
    char **princs;
    int n_princs;
    krb5_storage *sp;

    krb5_unparse_name_fixed(context->context, context->caller,
			    client, sizeof(client));

    sp = krb5_storage_from_data(in);
    if (sp == NULL)
	krb5_errx(context->context, 1, "out of memory");

    krb5_ret_int32(sp, &cmd);
    switch(cmd){
    case kadm_get:{
	op = "GET";
	ret = krb5_ret_principal(sp, &princ);
	if(ret)
	    goto fail;
	ret = krb5_ret_int32(sp, &mask);
	if(ret){
	    krb5_free_principal(context->context, princ);
	    goto fail;
	}
	mask |= KADM5_PRINCIPAL;
	krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
	krb5_warnx(context->context, "%s: %s %s", client, op, name);
	ret = _kadm5_acl_check_permission(context, KADM5_PRIV_GET, princ);
	if(ret){
	    krb5_free_principal(context->context, princ);
	    goto fail;
	}
	ret = kadm5_get_principal(kadm_handle, princ, &ent, mask);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	if(ret == 0){
	    kadm5_store_principal_ent(sp, &ent);
	    kadm5_free_principal_ent(kadm_handle, &ent);
	}
	krb5_free_principal(context->context, princ);
	break;
    }
    case kadm_delete:{
	op = "DELETE";
	ret = krb5_ret_principal(sp, &princ);
	if(ret)
	    goto fail;
	krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
	krb5_warnx(context->context, "%s: %s %s", client, op, name);
	ret = _kadm5_acl_check_permission(context, KADM5_PRIV_DELETE, princ);
	if(ret){
	    krb5_free_principal(context->context, princ);
	    goto fail;
	}
	ret = kadm5_delete_principal(kadm_handle, princ);
	krb5_free_principal(context->context, princ);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	break;
    }
    case kadm_create:{
	op = "CREATE";
	ret = kadm5_ret_principal_ent(sp, &ent);
	if(ret)
	    goto fail;
	ret = krb5_ret_int32(sp, &mask);
	if(ret){
	    kadm5_free_principal_ent(context->context, &ent);
	    goto fail;
	}
	ret = krb5_ret_string(sp, &password);
	if(ret){
	    kadm5_free_principal_ent(context->context, &ent);
	    goto fail;
	}
	krb5_unparse_name_fixed(context->context, ent.principal,
				name, sizeof(name));
	krb5_warnx(context->context, "%s: %s %s", client, op, name);
	ret = _kadm5_acl_check_permission(context, KADM5_PRIV_ADD,
					  ent.principal);
	if(ret){
	    kadm5_free_principal_ent(context->context, &ent);
	    memset(password, 0, strlen(password));
	    free(password);
	    goto fail;
	}
	ret = kadm5_create_principal(kadm_handle, &ent,
				     mask, password);
	kadm5_free_principal_ent(kadm_handle, &ent);
	memset(password, 0, strlen(password));
	free(password);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	break;
    }
    case kadm_modify:{
	op = "MODIFY";
	ret = kadm5_ret_principal_ent(sp, &ent);
	if(ret)
	    goto fail;
	ret = krb5_ret_int32(sp, &mask);
	if(ret){
	    kadm5_free_principal_ent(context, &ent);
	    goto fail;
	}
	krb5_unparse_name_fixed(context->context, ent.principal,
				name, sizeof(name));
	krb5_warnx(context->context, "%s: %s %s", client, op, name);
	ret = _kadm5_acl_check_permission(context, KADM5_PRIV_MODIFY,
					  ent.principal);
	if(ret){
	    kadm5_free_principal_ent(context, &ent);
	    goto fail;
	}
	ret = kadm5_modify_principal(kadm_handle, &ent, mask);
	kadm5_free_principal_ent(kadm_handle, &ent);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	break;
    }
    case kadm_rename:{
	op = "RENAME";
	ret = krb5_ret_principal(sp, &princ);
	if(ret)
	    goto fail;
	ret = krb5_ret_principal(sp, &princ2);
	if(ret){
	    krb5_free_principal(context->context, princ);
	    goto fail;
	}
	krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
	krb5_unparse_name_fixed(context->context, princ2, name2, sizeof(name2));
	krb5_warnx(context->context, "%s: %s %s -> %s",
		   client, op, name, name2);
	ret = _kadm5_acl_check_permission(context,
					  KADM5_PRIV_ADD,
					  princ2)
	    || _kadm5_acl_check_permission(context,
					   KADM5_PRIV_DELETE,
					   princ);
	if(ret){
	    krb5_free_principal(context->context, princ);
	    krb5_free_principal(context->context, princ2);
	    goto fail;
	}
	ret = kadm5_rename_principal(kadm_handle, princ, princ2);
	krb5_free_principal(context->context, princ);
	krb5_free_principal(context->context, princ2);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	break;
    }
    case kadm_chpass:{
	op = "CHPASS";
	ret = krb5_ret_principal(sp, &princ);
	if(ret)
	    goto fail;
	ret = krb5_ret_string(sp, &password);
	if(ret){
	    krb5_free_principal(context->context, princ);
	    goto fail;
	}
	krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
	krb5_warnx(context->context, "%s: %s %s", client, op, name);

	/*
	 * The change is allowed if at least one of:

	 * a) it's for the principal him/herself and this was an
	 *    initial ticket, but then, check with the password quality
	 *    function.
	 * b) the user is on the CPW ACL.
	 */

	if (initial
	    && krb5_principal_compare (context->context, context->caller,
				       princ))
	{
	    krb5_data pwd_data;
	    const char *pwd_reason;

	    pwd_data.data = password;
	    pwd_data.length = strlen(password);

	    pwd_reason = kadm5_check_password_quality (context->context,
						       princ, &pwd_data);
	    if (pwd_reason != NULL)
		ret = KADM5_PASS_Q_DICT;
	    else
		ret = 0;
	} else
	    ret = _kadm5_acl_check_permission(context, KADM5_PRIV_CPW, princ);

	if(ret) {
	    krb5_free_principal(context->context, princ);
	    memset(password, 0, strlen(password));
	    free(password);
	    goto fail;
	}
	ret = kadm5_chpass_principal(kadm_handle, princ, password);
	krb5_free_principal(context->context, princ);
	memset(password, 0, strlen(password));
	free(password);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	break;
    }
    case kadm_chpass_with_key:{
	int i;
	krb5_key_data *key_data;
	int n_key_data;

	op = "CHPASS_WITH_KEY";
	ret = krb5_ret_principal(sp, &princ);
	if(ret)
	    goto fail;
	ret = krb5_ret_int32(sp, &n_key_data);
	if (ret) {
	    krb5_free_principal(context->context, princ);
	    goto fail;
	}
	/* n_key_data will be squeezed into an int16_t below. */
	if (n_key_data < 0 || n_key_data >= 1 << 16 ||
	    n_key_data > UINT_MAX/sizeof(*key_data)) {
	    ret = ERANGE;
	    krb5_free_principal(context->context, princ);
	    goto fail;
	}

	key_data = malloc (n_key_data * sizeof(*key_data));
	if (key_data == NULL && n_key_data != 0) {
	    ret = ENOMEM;
	    krb5_free_principal(context->context, princ);
	    goto fail;
	}

	for (i = 0; i < n_key_data; ++i) {
	    ret = kadm5_ret_key_data (sp, &key_data[i]);
	    if (ret) {
		int16_t dummy = i;

		kadm5_free_key_data (context, &dummy, key_data);
		free (key_data);
		krb5_free_principal(context->context, princ);
		goto fail;
	    }
	}

	krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
	krb5_warnx(context->context, "%s: %s %s", client, op, name);

	/*
	 * The change is only allowed if the user is on the CPW ACL,
	 * this it to force password quality check on the user.
	 */

	ret = _kadm5_acl_check_permission(context, KADM5_PRIV_CPW, princ);
	if(ret) {
	    int16_t dummy = n_key_data;

	    kadm5_free_key_data (context, &dummy, key_data);
	    free (key_data);
	    krb5_free_principal(context->context, princ);
	    goto fail;
	}
	ret = kadm5_chpass_principal_with_key(kadm_handle, princ,
					      n_key_data, key_data);
	{
	    int16_t dummy = n_key_data;
	    kadm5_free_key_data (context, &dummy, key_data);
	}
	free (key_data);
	krb5_free_principal(context->context, princ);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	break;
    }
    case kadm_randkey:{
	op = "RANDKEY";
	ret = krb5_ret_principal(sp, &princ);
	if(ret)
	    goto fail;
	krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
	krb5_warnx(context->context, "%s: %s %s", client, op, name);
	/*
	 * The change is allowed if at least one of:
	 * a) it's for the principal him/herself and this was an initial ticket
	 * b) the user is on the CPW ACL.
	 */

	if (initial
	    && krb5_principal_compare (context->context, context->caller,
				       princ))
	    ret = 0;
	else
	    ret = _kadm5_acl_check_permission(context, KADM5_PRIV_CPW, princ);

	if(ret) {
	    krb5_free_principal(context->context, princ);
	    goto fail;
	}
	ret = kadm5_randkey_principal(kadm_handle, princ,
				      &new_keys, &n_keys);
	krb5_free_principal(context->context, princ);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	if(ret == 0){
	    int i;
	    krb5_store_int32(sp, n_keys);
	    for(i = 0; i < n_keys; i++){
		krb5_store_keyblock(sp, new_keys[i]);
		krb5_free_keyblock_contents(context->context, &new_keys[i]);
	    }
	    free(new_keys);
	}
	break;
    }
    case kadm_get_privs:{
	uint32_t privs;
	ret = kadm5_get_privs(kadm_handle, &privs);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	if(ret == 0)
	    krb5_store_uint32(sp, privs);
	break;
    }
    case kadm_get_princs:{
	op = "LIST";
	ret = krb5_ret_int32(sp, &tmp);
	if(ret)
	    goto fail;
	if(tmp){
	    ret = krb5_ret_string(sp, &expression);
	    if(ret)
		goto fail;
	}else
	    expression = NULL;
	krb5_warnx(context->context, "%s: %s %s", client, op,
		   expression ? expression : "*");
	ret = _kadm5_acl_check_permission(context, KADM5_PRIV_LIST, NULL);
	if(ret){
	    free(expression);
	    goto fail;
	}
	ret = kadm5_get_principals(kadm_handle, expression, &princs, &n_princs);
	free(expression);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, ret);
	if(ret == 0){
	    int i;
	    krb5_store_int32(sp, n_princs);
	    for(i = 0; i < n_princs; i++)
		krb5_store_string(sp, princs[i]);
	    kadm5_free_name_list(kadm_handle, princs, &n_princs);
	}
	break;
    }
    default:
	krb5_warnx(context->context, "%s: UNKNOWN OP %d", client, cmd);
	krb5_storage_free(sp);
	sp = krb5_storage_emem();
	krb5_store_int32(sp, KADM5_FAILURE);
	break;
    }
    krb5_storage_to_data(sp, out);
    krb5_storage_free(sp);
    return 0;
fail:
    krb5_warn(context->context, ret, "%s", op);
    krb5_storage_seek(sp, 0, SEEK_SET);
    krb5_store_int32(sp, ret);
    krb5_storage_to_data(sp, out);
    krb5_storage_free(sp);
    return 0;
}
Example #26
0
kadm5_ret_t
kadm5_c_randkey_principal(void *server_handle,
			  krb5_principal princ,
			  krb5_boolean keepold,
			  int n_ks_tuple,
			  krb5_key_salt_tuple *ks_tuple,
			  krb5_keyblock **new_keys,
			  int *n_keys)
{
    kadm5_client_context *context = server_handle;
    kadm5_ret_t ret;
    krb5_storage *sp;
    unsigned char buf[1536];
    int32_t tmp;
    size_t i;
    krb5_data reply;

    ret = _kadm5_connect(server_handle);
    if(ret)
	return ret;

    sp = krb5_storage_from_mem(buf, sizeof(buf));
    if (sp == NULL) {
	krb5_clear_error_message(context->context);
	return ENOMEM;
    }

    /*
     * NOTE WELL: This message is extensible.  It currently consists of:
     *
     *  - opcode (kadm_randkey)
     *  - principal name (princ)
     *
     * followed by optional items, each of which must be present if
     * there are any items following them that are also present:
     *
     *  - keepold boolean (whether to delete old kvnos)
     *  - number of key/salt type tuples
     *  - array of {enctype, salttype}
     *
     * Eventually we may add:
     *
     *  - opaque string2key parameters (salt, rounds, ...)
     */
    krb5_store_int32(sp, kadm_randkey);
    krb5_store_principal(sp, princ);

    if (keepold == TRUE || n_ks_tuple > 0)
	krb5_store_uint32(sp, keepold);
    if (n_ks_tuple > 0)
	krb5_store_uint32(sp, n_ks_tuple);
    for (i = 0; i < n_ks_tuple; i++) {
	krb5_store_int32(sp, ks_tuple[i].ks_enctype);
	krb5_store_int32(sp, ks_tuple[i].ks_salttype);
    }
    /* Future extensions go here */

    ret = _kadm5_client_send(context, sp);
    krb5_storage_free(sp);
    if (ret)
	return ret;
    ret = _kadm5_client_recv(context, &reply);
    if(ret)
	return ret;
    sp = krb5_storage_from_data(&reply);
    if (sp == NULL) {
	krb5_clear_error_message(context->context);
	krb5_data_free (&reply);
	return ENOMEM;
    }
    krb5_clear_error_message(context->context);
    krb5_ret_int32(sp, &tmp);
    ret = tmp;
    if(ret == 0){
	krb5_keyblock *k;

	krb5_ret_int32(sp, &tmp);
	if (tmp < 0) {
	    ret = EOVERFLOW;
	    goto out;
	}
	k = malloc(tmp * sizeof(*k));
	if (k == NULL) {
	    ret = ENOMEM;
	    goto out;
	}
	for(i = 0; i < tmp; i++)
	    krb5_ret_keyblock(sp, &k[i]);
	if (n_keys && new_keys) {
	    *n_keys = tmp;
	    *new_keys = k;
	}
    }
out:
    krb5_storage_free(sp);
    krb5_data_free (&reply);
    return ret;
}
Example #27
0
static krb5_error_code
mdb_value2entry(krb5_context context, krb5_data *data, hdb_entry *entry)
{
    krb5_error_code ret;
    krb5_storage *sp;
    uint32_t u32;
    uint16_t u16, num_keys, num_tl;
    size_t i, j;
    char *p = NULL;

    sp = krb5_storage_from_data(data);
    if (sp == NULL) {
	krb5_set_error_message(context, ENOMEM, "out of memory");
	return ENOMEM;
    }
    
    krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);

    /* 16: baselength */
    CHECK(ret = krb5_ret_uint16(sp, &u16));
    if (u16 != KDB_V1_BASE_LENGTH) { ret = EINVAL; goto out; }
    /* 32: attributes */
    CHECK(ret = krb5_ret_uint32(sp, &u32));
    entry->flags.postdate =	 !(u16 & KRB5_KDB_DISALLOW_POSTDATED);
    entry->flags.forwardable =	 !(u16 & KRB5_KDB_DISALLOW_FORWARDABLE);
    entry->flags.initial =	!!(u16 & KRB5_KDB_DISALLOW_TGT_BASED);
    entry->flags.renewable =	 !(u16 & KRB5_KDB_DISALLOW_RENEWABLE);
    entry->flags.proxiable =	 !(u16 & KRB5_KDB_DISALLOW_PROXIABLE);
    /* DUP_SKEY */
    entry->flags.invalid =	!!(u16 & KRB5_KDB_DISALLOW_ALL_TIX);
    entry->flags.require_preauth =!!(u16 & KRB5_KDB_REQUIRES_PRE_AUTH);
    entry->flags.require_hwauth =!!(u16 & KRB5_KDB_REQUIRES_HW_AUTH);
    entry->flags.server =	 !(u16 & KRB5_KDB_DISALLOW_SVR);
    entry->flags.change_pw = 	!!(u16 & KRB5_KDB_PWCHANGE_SERVICE);
    entry->flags.client =	   1; /* XXX */

    /* 32: max time */
    CHECK(ret = krb5_ret_uint32(sp, &u32));
    if (u32) {
	entry->max_life = malloc(sizeof(*entry->max_life));
	*entry->max_life = u32;
    }
    /* 32: max renewable time */
    CHECK(ret = krb5_ret_uint32(sp, &u32));
    if (u32) {
	entry->max_renew = malloc(sizeof(*entry->max_renew));
	*entry->max_renew = u32;
    }
    /* 32: client expire */
    CHECK(ret = krb5_ret_uint32(sp, &u32));
    if (u32) {
	entry->valid_end = malloc(sizeof(*entry->valid_end));
	*entry->valid_end = u32;
    }
    /* 32: passwd expire */
    CHECK(ret = krb5_ret_uint32(sp, &u32));
    if (u32) {
	entry->pw_end = malloc(sizeof(*entry->pw_end));
	*entry->pw_end = u32;
    }
    /* 32: last successful passwd */
    CHECK(ret = krb5_ret_uint32(sp, &u32));
    /* 32: last failed attempt */
    CHECK(ret = krb5_ret_uint32(sp, &u32));
    /* 32: num of failed attempts */
    CHECK(ret = krb5_ret_uint32(sp, &u32));
    /* 16: num tl data */
    CHECK(ret = krb5_ret_uint16(sp, &u16));
    num_tl = u16;
    /* 16: num key data */
    CHECK(ret = krb5_ret_uint16(sp, &u16));
    num_keys = u16;
    /* 16: principal length */
    CHECK(ret = krb5_ret_uint16(sp, &u16));
    /* length: principal */
    {
	p = malloc(u16 + 1);
	krb5_storage_read(sp, p, u16);
	p[u16] = '\0';
	CHECK(ret = krb5_parse_name(context, p, &entry->principal));
	free(p); p = NULL;
    }
    /* for num tl data times
           16: tl data type
           16: tl data length
           length: length */
    for (i = 0; i < num_tl; i++) {
	CHECK(ret = krb5_ret_uint16(sp, &u16));
	CHECK(ret = krb5_ret_uint16(sp, &u16));
	krb5_storage_seek(sp, u16, SEEK_CUR);
    }
    /* for num key data times
       16: version (num keyblocks)
       16: kvno
       for version times:
           16: type
           16: length
           length: keydata */
    for (i = 0; i < num_keys; i++) {
	int keep = 0;
	uint16_t version;
	void *ptr;

	CHECK(ret = krb5_ret_uint16(sp, &u16));
	version = u16;
	CHECK(ret = krb5_ret_uint16(sp, &u16));

	if (entry->kvno < u16) {
	    keep = 1;
	    entry->kvno = u16;
	    for (j = 0; j < entry->keys.len; j++) {
		free_Key(&entry->keys.val[j]);
		free(entry->keys.val);
		entry->keys.len = 0;
		entry->keys.val = NULL;
	    }
	} else if (entry->kvno == u16)
	    keep = 1;

	if (keep) {
	    Key *k;

	    ptr = realloc(entry->keys.val, sizeof(entry->keys.val[0]) * (entry->keys.len + 1));
	    if (ptr == NULL) {
		ret = ENOMEM;
		goto out;
	    }
	    entry->keys.val = ptr;

	    /* k points to current Key */
	    k = &entry->keys.val[entry->keys.len];

	    memset(k, 0, sizeof(*k));
	    entry->keys.len += 1;

	    entry->keys.val[i].mkvno = malloc(sizeof(*entry->keys.val[i].mkvno));
	    if (entry->keys.val[i].mkvno == NULL) {
		ret = ENOMEM;
		goto out;
	    }
	    *entry->keys.val[i].mkvno = 1;

	    for (j = 0; j < version; j++) {
		uint16_t type;
		CHECK(ret = krb5_ret_uint16(sp, &type));
		CHECK(ret = krb5_ret_uint16(sp, &u16));
		if (j == 0) { /* key */
		    k->key.keytype = type;
		    if (u16 < 2) {
			ret = EINVAL;
			goto out;
		    }
		    krb5_storage_seek(sp, 2, SEEK_CUR); /* skip real length */
		    k->key.keyvalue.length = u16 - 2;
		    k->key.keyvalue.data = malloc(k->key.keyvalue.length);
		    krb5_storage_read(sp, k->key.keyvalue.data, k->key.keyvalue.length);
		} else if (j == 1) { /* salt */
		    k->salt = calloc(1, sizeof(*k->salt));
		    if (k->salt == NULL) {
			ret = ENOMEM;
			goto out;
		    }
		    k->salt->type = type;
		    if (u16 != 0) {
			k->salt->salt.data = malloc(u16);
			if (k->salt->salt.data == NULL) {
			    ret = ENOMEM;
			    goto out;
			}
			k->salt->salt.length = u16;
			krb5_storage_read(sp, k->salt->salt.data, k->salt->salt.length);
		    }			
		    fix_salt(context, entry, entry->keys.len - 1);
		} else {
		    krb5_storage_seek(sp, u16, SEEK_CUR);
		}
	    }
	} else {
	    /* skip */
	    for (j = 0; j < version; j++) {
		CHECK(ret = krb5_ret_uint16(sp, &u16));
		CHECK(ret = krb5_ret_uint16(sp, &u16));
		krb5_storage_seek(sp, u16, u16);
	    }
	}
    }

    return 0;
 out:
    if (p)
	free(p);
    free_hdb_entry(entry);
    return ret;
}
Example #28
0
static int
send_diffs (krb5_context context, slave *s, int log_fd,
	    const char *database, uint32_t current_version)
{
    krb5_storage *sp;
    uint32_t ver;
    time_t timestamp;
    enum kadm_ops op;
    uint32_t len;
    off_t right, left;
    krb5_data data;
    int ret = 0;

    if (s->version == current_version) {
	krb5_warnx(context, "slave %s in sync already at version %ld",
		   s->name, (long)s->version);
	return 0;
    }

    if (s->flags & SLAVE_F_DEAD)
	return 0;

    /* if slave is a fresh client, starting over */
    if (s->version == 0) {
	krb5_warnx(context, "sending complete log to fresh slave %s",
		   s->name);
	return send_complete (context, s, database, current_version);
    }

    sp = kadm5_log_goto_end (log_fd);
    right = krb5_storage_seek(sp, 0, SEEK_CUR);
    for (;;) {
	ret = kadm5_log_previous (context, sp, &ver, &timestamp, &op, &len);
	if (ret)
	    krb5_err(context, 1, ret,
		     "send_diffs: failed to find previous entry");
	left = krb5_storage_seek(sp, -16, SEEK_CUR);
	if (ver == s->version)
	    return 0;
	if (ver == s->version + 1)
	    break;
	if (left == 0) {
	    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);
	}
    }

    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");
	slave_dead(context, 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(context, 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(context, s);
	return 1;
    }
    slave_seen(s);

    s->version = current_version;

    return 0;
}