Пример #1
0
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_mk_priv(krb5_context context,
             krb5_auth_context auth_context,
             const krb5_data *userdata,
             krb5_data *outbuf,
             krb5_replay_data *outdata)
{
    krb5_error_code ret;
    KRB_PRIV s;
    EncKrbPrivPart part;
    u_char *buf = NULL;
    size_t buf_size;
    size_t len = 0;
    krb5_crypto crypto;
    krb5_keyblock *key;
    krb5_replay_data rdata;

    if ((auth_context->flags &
            (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
            outdata == NULL)
        return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */

    if (auth_context->local_subkey)
        key = auth_context->local_subkey;
    else if (auth_context->remote_subkey)
        key = auth_context->remote_subkey;
    else
        key = auth_context->keyblock;

    memset(&rdata, 0, sizeof(rdata));

    part.user_data = *userdata;

    krb5_us_timeofday (context, &rdata.timestamp, &rdata.usec);

    if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
        part.timestamp = &rdata.timestamp;
        part.usec      = &rdata.usec;
    } else {
        part.timestamp = NULL;
        part.usec      = NULL;
    }

    if (auth_context->flags & KRB5_AUTH_CONTEXT_RET_TIME) {
        outdata->timestamp = rdata.timestamp;
        outdata->usec = rdata.usec;
    }

    if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
        rdata.seq = auth_context->local_seqnumber;
        part.seq_number = &rdata.seq;
    } else
        part.seq_number = NULL;

    if (auth_context->flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)
        outdata->seq = auth_context->local_seqnumber;

    part.s_address = auth_context->local_address;
    part.r_address = auth_context->remote_address;

    krb5_data_zero (&s.enc_part.cipher);

    ASN1_MALLOC_ENCODE(EncKrbPrivPart, buf, buf_size, &part, &len, ret);
    if (ret)
        goto fail;
    if (buf_size != len)
        krb5_abortx(context, "internal error in ASN.1 encoder");

    s.pvno = 5;
    s.msg_type = krb_priv;
    s.enc_part.etype = key->keytype;
    s.enc_part.kvno = NULL;

    ret = krb5_crypto_init(context, key, 0, &crypto);
    if (ret) {
        free (buf);
        return ret;
    }
    ret = krb5_encrypt (context,
                        crypto,
                        KRB5_KU_KRB_PRIV,
                        buf + buf_size - len,
                        len,
                        &s.enc_part.cipher);
    krb5_crypto_destroy(context, crypto);
    if (ret) {
        free(buf);
        return ret;
    }
    free(buf);


    ASN1_MALLOC_ENCODE(KRB_PRIV, buf, buf_size, &s, &len, ret);
    if (ret)
        goto fail;
    if (buf_size != len)
        krb5_abortx(context, "internal error in ASN.1 encoder");

    krb5_data_free (&s.enc_part.cipher);

    ret = krb5_data_copy(outbuf, buf + buf_size - len, len);
    if (ret) {
        free(buf);
        return krb5_enomem(context);
    }
    free (buf);
    if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE)
        auth_context->local_seqnumber =
            (auth_context->local_seqnumber + 1) & 0xFFFFFFFF;
    return 0;

fail:
    free (buf);
    krb5_data_free (&s.enc_part.cipher);
    return ret;
}
Пример #2
0
static int
make_path(krb5_context context, struct tr_realm *r,
	  const char *from, const char *to)
{
    struct tr_realm *tmp;
    const char *p;

    if(strlen(from) < strlen(to)){
	const char *str;
	str = from;
	from = to;
	to = str;
    }

    if(strcmp(from + strlen(from) - strlen(to), to) == 0){
	p = from;
	while(1){
	    p = strchr(p, '.');
	    if(p == NULL) {
		krb5_clear_error_message (context);
		return KRB5KDC_ERR_POLICY;
	    }
	    p++;
	    if(strcmp(p, to) == 0)
		break;
	    tmp = calloc(1, sizeof(*tmp));
	    if(tmp == NULL)
		return krb5_enomem(context);
	    tmp->next = r->next;
	    r->next = tmp;
	    tmp->realm = strdup(p);
	    if(tmp->realm == NULL){
		r->next = tmp->next;
		free(tmp);
		return krb5_enomem(context);
	    }
	}
    }else if(strncmp(from, to, strlen(to)) == 0){
	p = from + strlen(from);
	while(1){
	    while(p >= from && *p != '/') p--;
	    if(p == from)
		return KRB5KDC_ERR_POLICY;

	    if(strncmp(to, from, p - from) == 0)
		break;
	    tmp = calloc(1, sizeof(*tmp));
	    if(tmp == NULL)
		return krb5_enomem(context);
	    tmp->next = r->next;
	    r->next = tmp;
	    tmp->realm = malloc(p - from + 1);
	    if(tmp->realm == NULL){
		r->next = tmp->next;
		free(tmp);
		return krb5_enomem(context);
	    }
	    memcpy(tmp->realm, from, p - from);
	    tmp->realm[p - from] = '\0';
	    p--;
	}
    } else {
	krb5_clear_error_message (context);
	return KRB5KDC_ERR_POLICY;
    }

    return 0;
}
Пример #3
0
static krb5_error_code
build_logon_name(krb5_context context,
		 time_t authtime,
		 krb5_const_principal principal,
		 krb5_data *logon)
{
    krb5_error_code ret;
    krb5_storage *sp;
    uint64_t t;
    char *s, *s2;
    size_t s2_len;

    t = unix2nttime(authtime);

    krb5_data_zero(logon);

    sp = krb5_storage_emem();
    if (sp == NULL)
	return krb5_enomem(context);

    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);

    CHECK(ret, krb5_store_uint32(sp, t & 0xffffffff), out);
    CHECK(ret, krb5_store_uint32(sp, t >> 32), out);

    ret = krb5_unparse_name_flags(context, principal,
				  KRB5_PRINCIPAL_UNPARSE_NO_REALM |
				  KRB5_PRINCIPAL_UNPARSE_DISPLAY,
				  &s);
    if (ret)
	goto out;

    {
	size_t ucs2_len;
	uint16_t *ucs2;
	unsigned int flags;

	ret = wind_utf8ucs2_length(s, &ucs2_len);
	if (ret) {
	    krb5_set_error_message(context, ret, "Principal %s is not valid UTF-8", s);
	    free(s);
	    return ret;
	}

	ucs2 = malloc(sizeof(ucs2[0]) * ucs2_len);
	if (ucs2 == NULL) {
	    free(s);
	    return krb5_enomem(context);
	}

	ret = wind_utf8ucs2(s, ucs2, &ucs2_len);
	if (ret) {
	    free(ucs2);
	    krb5_set_error_message(context, ret, "Principal %s is not valid UTF-8", s);
	    free(s);
	    return ret;
	} else 
	    free(s);

	s2_len = (ucs2_len + 1) * 2;
	s2 = malloc(s2_len);
	if (s2 == NULL) {
	    free(ucs2);
	    return krb5_enomem(context);
	}

	flags = WIND_RW_LE;
	ret = wind_ucs2write(ucs2, ucs2_len,
			     &flags, s2, &s2_len);
	free(ucs2);
	if (ret) {
	    free(s2);
	    krb5_set_error_message(context, ret, "Failed to write to UCS-2 buffer");
	    return ret;
	}

	/*
	 * we do not want zero termination
	 */
	s2_len = ucs2_len * 2;
    }

    CHECK(ret, krb5_store_uint16(sp, s2_len), out);

    ret = krb5_storage_write(sp, s2, s2_len);
    free(s2);
    if (ret != (int)s2_len) {
	ret = krb5_enomem(context);
	goto out;
    }
    ret = krb5_storage_to_data(sp, logon);
    if (ret)
	goto out;
    krb5_storage_free(sp);

    return 0;
out:
    krb5_storage_free(sp);
    return ret;
}
Пример #4
0
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_pac_sign(krb5_context context,
	       krb5_pac p,
	       time_t authtime,
	       krb5_principal principal,
	       const krb5_keyblock *server_key,
	       const krb5_keyblock *priv_key,
	       krb5_data *data)
{
    krb5_error_code ret;
    krb5_storage *sp = NULL, *spdata = NULL;
    uint32_t end;
    size_t server_size, priv_size;
    uint32_t server_offset = 0, priv_offset = 0;
    uint32_t server_cksumtype = 0, priv_cksumtype = 0;
    int num = 0;
    size_t i;
    krb5_data logon, d;

    krb5_data_zero(&logon);

    for (i = 0; i < p->pac->numbuffers; i++) {
	if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
	    if (p->server_checksum == NULL) {
		p->server_checksum = &p->pac->buffers[i];
	    }
	    if (p->server_checksum != &p->pac->buffers[i]) {
		ret = EINVAL;
		krb5_set_error_message(context, ret,
				       N_("PAC have two server checksums", ""));
		goto out;
	    }
	} else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
	    if (p->privsvr_checksum == NULL) {
		p->privsvr_checksum = &p->pac->buffers[i];
	    }
	    if (p->privsvr_checksum != &p->pac->buffers[i]) {
		ret = EINVAL;
		krb5_set_error_message(context, ret,
				       N_("PAC have two KDC checksums", ""));
		goto out;
	    }
	} else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
	    if (p->logon_name == NULL) {
		p->logon_name = &p->pac->buffers[i];
	    }
	    if (p->logon_name != &p->pac->buffers[i]) {
		ret = EINVAL;
		krb5_set_error_message(context, ret,
				       N_("PAC have two logon names", ""));
		goto out;
	    }
	}
    }

    if (p->logon_name == NULL)
	num++;
    if (p->server_checksum == NULL)
	num++;
    if (p->privsvr_checksum == NULL)
	num++;

    if (num) {
	void *ptr;

	ptr = realloc(p->pac, sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (p->pac->numbuffers + num - 1)));
	if (ptr == NULL)
	    return krb5_enomem(context);

	p->pac = ptr;

	if (p->logon_name == NULL) {
	    p->logon_name = &p->pac->buffers[p->pac->numbuffers++];
	    memset(p->logon_name, 0, sizeof(*p->logon_name));
	    p->logon_name->type = PAC_LOGON_NAME;
	}
	if (p->server_checksum == NULL) {
	    p->server_checksum = &p->pac->buffers[p->pac->numbuffers++];
	    memset(p->server_checksum, 0, sizeof(*p->server_checksum));
	    p->server_checksum->type = PAC_SERVER_CHECKSUM;
	}
	if (p->privsvr_checksum == NULL) {
	    p->privsvr_checksum = &p->pac->buffers[p->pac->numbuffers++];
	    memset(p->privsvr_checksum, 0, sizeof(*p->privsvr_checksum));
	    p->privsvr_checksum->type = PAC_PRIVSVR_CHECKSUM;
	}
    }

    /* Calculate LOGON NAME */
    ret = build_logon_name(context, authtime, principal, &logon);
    if (ret)
	goto out;

    /* Set lengths for checksum */
    ret = pac_checksum(context, server_key, &server_cksumtype, &server_size);
    if (ret)
	goto out;
    ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size);
    if (ret)
	goto out;

    /* Encode PAC */
    sp = krb5_storage_emem();
    if (sp == NULL)
	return krb5_enomem(context);

    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);

    spdata = krb5_storage_emem();
    if (spdata == NULL) {
	krb5_storage_free(sp);
	return krb5_enomem(context);
    }
    krb5_storage_set_flags(spdata, KRB5_STORAGE_BYTEORDER_LE);

    CHECK(ret, krb5_store_uint32(sp, p->pac->numbuffers), out);
    CHECK(ret, krb5_store_uint32(sp, p->pac->version), out);

    end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);

    for (i = 0; i < p->pac->numbuffers; i++) {
	uint32_t len;
	size_t sret;
	void *ptr = NULL;

	/* store data */

	if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
	    len = server_size + 4;
	    server_offset = end + 4;
	    CHECK(ret, krb5_store_uint32(spdata, server_cksumtype), out);
	    CHECK(ret, fill_zeros(context, spdata, server_size), out);
	} else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
	    len = priv_size + 4;
	    priv_offset = end + 4;
	    CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out);
	    CHECK(ret, fill_zeros(context, spdata, priv_size), out);
	} else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
	    len = krb5_storage_write(spdata, logon.data, logon.length);
	    if (logon.length != len) {
		ret = EINVAL;
		goto out;
	    }
	} else {
	    len = p->pac->buffers[i].buffersize;
	    ptr = (char *)p->data.data + p->pac->buffers[i].offset_lo;

	    sret = krb5_storage_write(spdata, ptr, len);
	    if (sret != len) {
		ret = krb5_enomem(context);
		goto out;
	    }
	    /* XXX if not aligned, fill_zeros */
	}

	/* write header */
	CHECK(ret, krb5_store_uint32(sp, p->pac->buffers[i].type), out);
	CHECK(ret, krb5_store_uint32(sp, len), out);
	CHECK(ret, krb5_store_uint32(sp, end), out);
	CHECK(ret, krb5_store_uint32(sp, 0), out);

	/* advance data endpointer and align */
	{
	    int32_t e;

	    end += len;
	    e = ((end + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT;
	    if ((int32_t)end != e) {
		CHECK(ret, fill_zeros(context, spdata, e - end), out);
	    }
	    end = e;
	}

    }

    /* assert (server_offset != 0 && priv_offset != 0); */

    /* export PAC */
    ret = krb5_storage_to_data(spdata, &d);
    if (ret) {
	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
	goto out;
    }
    ret = krb5_storage_write(sp, d.data, d.length);
    if (ret != (int)d.length) {
	krb5_data_free(&d);
	ret = krb5_enomem(context);
	goto out;
    }
    krb5_data_free(&d);

    ret = krb5_storage_to_data(sp, &d);
    if (ret) {
	ret = krb5_enomem(context);
	goto out;
    }

    /* sign */
    ret = create_checksum(context, server_key, server_cksumtype,
			  d.data, d.length,
			  (char *)d.data + server_offset, server_size);
    if (ret) {
	krb5_data_free(&d);
	goto out;
    }
    ret = create_checksum(context, priv_key, priv_cksumtype,
			  (char *)d.data + server_offset, server_size,
			  (char *)d.data + priv_offset, priv_size);
    if (ret) {
	krb5_data_free(&d);
	goto out;
    }

    /* done */
    *data = d;

    krb5_data_free(&logon);
    krb5_storage_free(sp);
    krb5_storage_free(spdata);

    return 0;
out:
    krb5_data_free(&logon);
    if (sp)
	krb5_storage_free(sp);
    if (spdata)
	krb5_storage_free(spdata);
    return ret;
}
Пример #5
0
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_copy_context(krb5_context context, krb5_context *out)
{
    krb5_error_code ret;
    krb5_context p;

    *out = NULL;

    p = calloc(1, sizeof(*p));
    if (p == NULL)
	return krb5_enomem(context);

    p->mutex = malloc(sizeof(HEIMDAL_MUTEX));
    if (p->mutex == NULL) {
	free(p);
	return krb5_enomem(context);
    }
    HEIMDAL_MUTEX_init(p->mutex);


    if (context->default_cc_name)
	p->default_cc_name = strdup(context->default_cc_name);
    if (context->default_cc_name_env)
	p->default_cc_name_env = strdup(context->default_cc_name_env);

    if (context->etypes) {
	ret = copy_etypes(context, context->etypes, &p->etypes);
	if (ret)
	    goto out;
    }
    if (context->etypes_des) {
	ret = copy_etypes(context, context->etypes_des, &p->etypes_des);
	if (ret)
	    goto out;
    }

    if (context->default_realms) {
	ret = krb5_copy_host_realm(context,
				   context->default_realms, &p->default_realms);
	if (ret)
	    goto out;
    }

    ret = _krb5_config_copy(context, context->cf, &p->cf);
    if (ret)
	goto out;

    /* XXX should copy */
    krb5_init_ets(p);

    cc_ops_copy(p, context);
    kt_ops_copy(p, context);

#if 0 /* XXX */
    if(context->warn_dest != NULL)
	;
    if(context->debug_dest != NULL)
	;
#endif

    ret = krb5_set_extra_addresses(p, context->extra_addresses);
    if (ret)
	goto out;
    ret = krb5_set_extra_addresses(p, context->ignore_addresses);
    if (ret)
	goto out;

    ret = _krb5_copy_send_to_kdc_func(p, context);
    if (ret)
	goto out;

    *out = p;

    return 0;

 out:
    krb5_free_context(p);
    return ret;
}
Пример #6
0
static krb5_error_code
srv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count,
	       const char *realm, const char *dns_type,
	       const char *proto, const char *service, int port)
{
    char domain[1024];
    struct rk_dns_reply *r;
    struct rk_resource_record *rr;
    int num_srv;
    int proto_num;
    int def_port;

    *res = NULL;
    *count = 0;

    proto_num = string_to_proto(proto);
    if(proto_num < 0) {
	krb5_set_error_message(context, EINVAL,
			       N_("unknown protocol `%s' to lookup", ""),
			       proto);
	return EINVAL;
    }

    if(proto_num == KRB5_KRBHST_HTTP)
	def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80));
    else if(port == 0)
	def_port = ntohs(krb5_getportbyname (context, service, proto, 88));
    else
	def_port = port;

    snprintf(domain, sizeof(domain), "_%s._%s.%s.", service, proto, realm);

    r = rk_dns_lookup(domain, dns_type);
    if(r == NULL) {
	_krb5_debug(context, 0,
		    "DNS lookup failed domain: %s", domain);
	return KRB5_KDC_UNREACH;
    }

    for(num_srv = 0, rr = r->head; rr; rr = rr->next)
	if(rr->type == rk_ns_t_srv)
	    num_srv++;

    *res = malloc(num_srv * sizeof(**res));
    if(*res == NULL) {
	rk_dns_free_data(r);
	return krb5_enomem(context);
    }

    rk_dns_srv_order(r);

    for(num_srv = 0, rr = r->head; rr; rr = rr->next)
	if(rr->type == rk_ns_t_srv) {
	    krb5_krbhst_info *hi;
	    size_t len = strlen(rr->u.srv->target);

	    hi = calloc(1, sizeof(*hi) + len);
	    if(hi == NULL) {
		rk_dns_free_data(r);
		while(--num_srv >= 0)
		    free((*res)[num_srv]);
		free(*res);
		*res = NULL;
		return krb5_enomem(context);
	    }
	    (*res)[num_srv++] = hi;

	    hi->proto = proto_num;

	    hi->def_port = def_port;
	    if (port != 0)
		hi->port = port;
	    else
		hi->port = rr->u.srv->port;

	    strlcpy(hi->hostname, rr->u.srv->target, len + 1);
	}

    *count = num_srv;

    rk_dns_free_data(r);
    return 0;
}
Пример #7
0
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_pac_parse(krb5_context context, const void *ptr, size_t len,
	       krb5_pac *pac)
{
    krb5_error_code ret;
    krb5_pac p;
    krb5_storage *sp = NULL;
    uint32_t i, tmp, tmp2, header_end;

    p = calloc(1, sizeof(*p));
    if (p == NULL) {
	ret = krb5_enomem(context);
	goto out;
    }

    sp = krb5_storage_from_readonly_mem(ptr, len);
    if (sp == NULL) {
	ret = krb5_enomem(context);
	goto out;
    }
    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);

    CHECK(ret, krb5_ret_uint32(sp, &tmp), out);
    CHECK(ret, krb5_ret_uint32(sp, &tmp2), out);
    if (tmp < 1) {
	ret = EINVAL; /* Too few buffers */
	krb5_set_error_message(context, ret, N_("PAC have too few buffer", ""));
	goto out;
    }
    if (tmp2 != 0) {
	ret = EINVAL; /* Wrong version */
	krb5_set_error_message(context, ret,
			       N_("PAC have wrong version %d", ""),
			       (int)tmp2);
	goto out;
    }

    p->pac = calloc(1,
		    sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (tmp - 1)));
    if (p->pac == NULL) {
	ret = krb5_enomem(context);
	goto out;
    }

    p->pac->numbuffers = tmp;
    p->pac->version = tmp2;

    header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
    if (header_end > len) {
	ret = EINVAL;
	goto out;
    }

    for (i = 0; i < p->pac->numbuffers; i++) {
	CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].type), out);
	CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].buffersize), out);
	CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_lo), out);
	CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_hi), out);

	/* consistency checks */
	if (p->pac->buffers[i].offset_lo & (PAC_ALIGNMENT - 1)) {
	    ret = EINVAL;
	    krb5_set_error_message(context, ret,
				   N_("PAC out of allignment", ""));
	    goto out;
	}
	if (p->pac->buffers[i].offset_hi) {
	    ret = EINVAL;
	    krb5_set_error_message(context, ret,
				   N_("PAC high offset set", ""));
	    goto out;
	}
	if (p->pac->buffers[i].offset_lo > len) {
	    ret = EINVAL;
	    krb5_set_error_message(context, ret,
				   N_("PAC offset off end", ""));
	    goto out;
	}
	if (p->pac->buffers[i].offset_lo < header_end) {
	    ret = EINVAL;
	    krb5_set_error_message(context, ret,
				   N_("PAC offset inside header: %lu %lu", ""),
				   (unsigned long)p->pac->buffers[i].offset_lo,
				   (unsigned long)header_end);
	    goto out;
	}
	if (p->pac->buffers[i].buffersize > len - p->pac->buffers[i].offset_lo){
	    ret = EINVAL;
	    krb5_set_error_message(context, ret, N_("PAC length off end", ""));
	    goto out;
	}

	/* let save pointer to data we need later */
	if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
	    if (p->server_checksum) {
		ret = EINVAL;
		krb5_set_error_message(context, ret,
				       N_("PAC have two server checksums", ""));
		goto out;
	    }
	    p->server_checksum = &p->pac->buffers[i];
	} else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
	    if (p->privsvr_checksum) {
		ret = EINVAL;
		krb5_set_error_message(context, ret,
				       N_("PAC have two KDC checksums", ""));
		goto out;
	    }
	    p->privsvr_checksum = &p->pac->buffers[i];
	} else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
	    if (p->logon_name) {
		ret = EINVAL;
		krb5_set_error_message(context, ret,
				       N_("PAC have two logon names", ""));
		goto out;
	    }
	    p->logon_name = &p->pac->buffers[i];
	}
    }

    ret = krb5_data_copy(&p->data, ptr, len);
    if (ret)
	goto out;

    krb5_storage_free(sp);

    *pac = p;
    return 0;

out:
    if (sp)
	krb5_storage_free(sp);
    if (p) {
	if (p->pac)
	    free(p->pac);
	free(p);
    }
    *pac = NULL;

    return ret;
}
Пример #8
0
krb5_error_code
_kdc_db_fetch(krb5_context context,
	      krb5_kdc_configuration *config,
	      krb5_const_principal principal,
	      unsigned flags,
	      krb5int32 *kvno_ptr,
	      HDB **db,
	      hdb_entry_ex **h)
{
    hdb_entry_ex *ent;
    krb5_error_code ret = HDB_ERR_NOENTRY;
    int i;
    unsigned kvno = 0;
    krb5_principal enterprise_principal = NULL;
    krb5_const_principal princ;

    *h = NULL;

    if (kvno_ptr) {
	    kvno = *kvno_ptr;
	    flags |= HDB_F_KVNO_SPECIFIED;
    }

    ent = calloc(1, sizeof (*ent));
    if (ent == NULL)
        return krb5_enomem(context);

    if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
        if (principal->name.name_string.len != 1) {
            ret = KRB5_PARSE_MALFORMED;
            krb5_set_error_message(context, ret,
                                   "malformed request: "
                                   "enterprise name with %d name components",
                                   principal->name.name_string.len);
            goto out;
        }
        ret = krb5_parse_name(context, principal->name.name_string.val[0],
                              &enterprise_principal);
        if (ret)
            goto out;
    }

    for (i = 0; i < config->num_db; i++) {
	ret = config->db[i]->hdb_open(context, config->db[i], O_RDONLY, 0);
	if (ret) {
	    const char *msg = krb5_get_error_message(context, ret);
	    kdc_log(context, config, 0, "Failed to open database: %s", msg);
	    krb5_free_error_message(context, msg);
	    continue;
	}

        princ = principal;
        if (!(config->db[i]->hdb_capability_flags & HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL) && enterprise_principal)
            princ = enterprise_principal;

	ret = config->db[i]->hdb_fetch_kvno(context,
					    config->db[i],
					    princ,
					    flags | HDB_F_DECRYPT,
					    kvno,
					    ent);
	config->db[i]->hdb_close(context, config->db[i]);

	switch (ret) {
	case HDB_ERR_WRONG_REALM:
	    /*
	     * the ent->entry.principal just contains hints for the client
	     * to retry. This is important for enterprise principal routing
	     * between trusts.
	     */
	    /* fall through */
	case 0:
	    if (db)
		*db = config->db[i];
	    *h = ent;
            ent = NULL;
            goto out;

	case HDB_ERR_NOENTRY:
	    /* Check the other databases */
	    continue;

	default:
	    /* 
	     * This is really important, because errors like
	     * HDB_ERR_NOT_FOUND_HERE (used to indicate to Samba that
	     * the RODC on which this code is running does not have
	     * the key we need, and so a proxy to the KDC is required)
	     * have specific meaning, and need to be propogated up.
	     */
	    goto out;
	}
    }

    if (ret == HDB_ERR_NOENTRY) {
	krb5_set_error_message(context, ret, "no such entry found in hdb");
    }
out:
    krb5_free_principal(context, enterprise_principal);
    free(ent);
    return ret;
}
Пример #9
0
static krb5_error_code KRB5_CALLCONV
akf_add_entry(krb5_context context,
	      krb5_keytab id,
	      krb5_keytab_entry *entry)
{
    struct akf_data *d = id->data;
    int fd, created = 0;
    krb5_error_code ret;
    int32_t len;
    krb5_storage *sp;


    if (entry->keyblock.keyvalue.length != 8)
	return 0;
    switch(entry->keyblock.keytype) {
    case ETYPE_DES_CBC_CRC:
    case ETYPE_DES_CBC_MD4:
    case ETYPE_DES_CBC_MD5:
	break;
    default:
	return 0;
    }

    fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC);
    if (fd < 0) {
	fd = open (d->filename,
		   O_RDWR | O_BINARY | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
	if (fd < 0) {
	    ret = errno;
	    krb5_set_error_message(context, ret,
				   N_("open keyfile(%s): %s", ""),
				   d->filename,
				   strerror(ret));
	    return ret;
	}
	created = 1;
    }

    sp = krb5_storage_from_fd(fd);
    if(sp == NULL) {
	close(fd);
	return krb5_enomem(context);
    }
    if (created)
	len = 0;
    else {
	if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
	    ret = errno;
	    krb5_storage_free(sp);
	    close(fd);
	    krb5_set_error_message(context, ret,
				   N_("seeking in keyfile: %s", ""),
				   strerror(ret));
	    return ret;
	}

	ret = krb5_ret_int32(sp, &len);
	if(ret) {
	    krb5_storage_free(sp);
	    close(fd);
	    return ret;
	}
    }

    /*
     * Make sure we don't add the entry twice, assumes the DES
     * encryption types are all the same key.
     */
    if (len > 0) {
	int32_t kvno;
	int i;

	for (i = 0; i < len; i++) {
	    ret = krb5_ret_int32(sp, &kvno);
	    if (ret) {
		krb5_set_error_message (context, ret,
					N_("Failed getting kvno from keyfile", ""));
		goto out;
	    }
	    if(krb5_storage_seek(sp, 8, SEEK_CUR) < 0) {
		ret = errno;
		krb5_set_error_message (context, ret,
					N_("Failed seeing in keyfile: %s", ""),
					strerror(ret));
		goto out;
	    }
	    if (kvno == entry->vno) {
		ret = 0;
		goto out;
	    }
	}
    }

    len++;

    if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
	ret = errno;
	krb5_set_error_message (context, ret,
				N_("Failed seeing in keyfile: %s", ""),
				strerror(ret));
	goto out;
    }

    ret = krb5_store_int32(sp, len);
    if(ret) {
	ret = errno;
	krb5_set_error_message (context, ret,
				N_("keytab keyfile failed new length", ""));
	return ret;
    }

    if(krb5_storage_seek(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) {
	ret = errno;
	krb5_set_error_message (context, ret,
				N_("seek to end: %s", ""), strerror(ret));
	goto out;
    }

    ret = krb5_store_int32(sp, entry->vno);
    if(ret) {
	krb5_set_error_message(context, ret,
			       N_("keytab keyfile failed store kvno", ""));
	goto out;
    }
    ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data,
			     entry->keyblock.keyvalue.length);
    if(ret != entry->keyblock.keyvalue.length) {
	if (ret < 0)
	    ret = errno;
	else
	    ret = ENOTTY;
	krb5_set_error_message(context, ret,
			       N_("keytab keyfile failed to add key", ""));
	goto out;
    }
    ret = 0;
out:
    krb5_storage_free(sp);
    close (fd);
    return ret;
}
Пример #10
0
/**
 * Internal function to expand tokens in paths.
 *
 * Inputs:
 *
 * @context   A krb5_context
 * @path_in   The path to expand tokens from
 * @ppath_out The expanded path
 * @...       Variable number of pairs of strings, the first of each
 *            being a token (e.g., "luser") and the second a string to
 *            replace it with.  The list is terminated by a NULL.
 * 
 * Outputs:
 *
 * @ppath_out Path with expanded tokens (caller must free() this)
 */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_expand_path_tokensv(krb5_context context,
			  const char *path_in,
			  char **ppath_out, ...)
{
    char *tok_begin, *tok_end, *append;
    char **extra_tokens = NULL;
    const char *path_left;
    const char *s;
    size_t nargs = 0;
    size_t len = 0;
    va_list ap;

    if (path_in == NULL || *path_in == '\0') {
        *ppath_out = strdup("");
        return 0;
    }

    *ppath_out = NULL;

    va_start(ap, ppath_out);
    while ((s = va_arg(ap, const char *))) {
	nargs++;
        s = va_arg(ap, const char *);
    }
    va_end(ap);
    nargs *= 2;

    /* Get extra tokens */
    if (nargs) {
	size_t i;

	extra_tokens = calloc(nargs + 1, sizeof (*extra_tokens));
	if (extra_tokens == NULL)
	    return krb5_enomem(context);
	va_start(ap, ppath_out);
	for (i = 0; i < nargs; i++) {
	    s = va_arg(ap, const char *); /* token key */
	    if (s == NULL)
		break;
	    extra_tokens[i] = strdup(s);
	    if (extra_tokens[i++] == NULL) {
		free_extra_tokens(extra_tokens);
		return krb5_enomem(context);
	    }
	    s = va_arg(ap, const char *); /* token value */
	    if (s == NULL)
		s = "";
	    extra_tokens[i] = strdup(s);
	    if (extra_tokens[i] == NULL) {
		free_extra_tokens(extra_tokens);
		return krb5_enomem(context);
	    }
	}
	va_end(ap);
    }

    for (path_left = path_in; path_left && *path_left; ) {

	tok_begin = strstr(path_left, "%{");

	if (tok_begin && tok_begin != path_left) {

	    append = malloc((tok_begin - path_left) + 1);
	    if (append) {
		memcpy(append, path_left, tok_begin - path_left);
		append[tok_begin - path_left] = '\0';
	    }
	    path_left = tok_begin;

	} else if (tok_begin) {

	    tok_end = strchr(tok_begin, '}');
	    if (tok_end == NULL) {
		free_extra_tokens(extra_tokens);
		if (*ppath_out)
		    free(*ppath_out);
		*ppath_out = NULL;
		if (context)
		    krb5_set_error_message(context, EINVAL, "variable missing }");
		return EINVAL;
	    }

	    if (_expand_token(context, tok_begin, tok_end, extra_tokens,
			      &append)) {
		free_extra_tokens(extra_tokens);
		if (*ppath_out)
		    free(*ppath_out);
		*ppath_out = NULL;
		return EINVAL;
	    }

	    path_left = tok_end + 1;
	} else {

	    append = strdup(path_left);
	    path_left = NULL;

	}

	if (append == NULL) {

	    free_extra_tokens(extra_tokens);
	    if (*ppath_out)
		free(*ppath_out);
	    *ppath_out = NULL;
	    return krb5_enomem(context);

	}

	{
	    size_t append_len = strlen(append);
	    char * new_str = realloc(*ppath_out, len + append_len + 1);

	    if (new_str == NULL) {
		free_extra_tokens(extra_tokens);
		free(append);
		if (*ppath_out)
		    free(*ppath_out);
		*ppath_out = NULL;
		return krb5_enomem(context);
	    }

	    *ppath_out = new_str;
	    memcpy(*ppath_out + len, append, append_len + 1);
	    len = len + append_len;
	    free(append);
	}
    }

#ifdef _WIN32
    /* Also deal with slashes */
    if (*ppath_out) {
	char * c;
	for (c = *ppath_out; *c; c++)
	    if (*c == '/')
		*c = '\\';
    }
#endif

    free_extra_tokens(extra_tokens);
    return 0;
}
Пример #11
0
static krb5_error_code
AES_SHA2_string_to_key(krb5_context context,
		       krb5_enctype enctype,
		       krb5_data password,
		       krb5_salt salt,
		       krb5_data opaque,
		       krb5_keyblock *key)
{
    krb5_error_code ret;
    uint32_t iter;
    struct _krb5_encryption_type *et = NULL;
    struct _krb5_key_data kd;
    krb5_data saltp;
    size_t enctypesz;
    const EVP_MD *md = NULL;

    krb5_data_zero(&saltp);
    kd.key = NULL;
    kd.schedule = NULL;

    if (opaque.length == 0) {
	iter = _krb5_AES_SHA2_string_to_default_iterator;
    } else if (opaque.length == 4) {
	unsigned long v;
	_krb5_get_int(opaque.data, &v, 4);
	iter = ((uint32_t)v);
    } else {
	ret = KRB5_PROG_KEYTYPE_NOSUPP; /* XXX */
	goto cleanup;
    }

    et = _krb5_find_enctype(enctype);
    if (et == NULL) {
	ret = KRB5_PROG_KEYTYPE_NOSUPP;
	goto cleanup;
    }

    kd.schedule = NULL;
    ALLOC(kd.key, 1);
    if (kd.key == NULL) {
	ret = krb5_enomem(context);
	goto cleanup;
    }
    kd.key->keytype = enctype;
    ret = krb5_data_alloc(&kd.key->keyvalue, et->keytype->size);
    if (ret) {
	ret = krb5_enomem(context);
	goto cleanup;
    }

    enctypesz = strlen(et->name) + 1;
    ret = krb5_data_alloc(&saltp, enctypesz + salt.saltvalue.length);
    if (ret) {
	ret = krb5_enomem(context);
	goto cleanup;
    }
    memcpy(saltp.data, et->name, enctypesz);
    memcpy((unsigned char *)saltp.data + enctypesz,
	   salt.saltvalue.data, salt.saltvalue.length);

    ret = _krb5_aes_sha2_md_for_enctype(context, enctype, &md);
    if (ret)
	goto cleanup;

    ret = PKCS5_PBKDF2_HMAC(password.data, password.length,
			    saltp.data, saltp.length,
			    iter, md,
			    et->keytype->size, kd.key->keyvalue.data);
    if (ret != 1) {
	krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP,
			       "Error calculating s2k");
	ret = KRB5_PROG_KEYTYPE_NOSUPP;
	goto cleanup;
    }

    ret = _krb5_derive_key(context, et, &kd, "kerberos", strlen("kerberos"));
    if (ret)
	goto cleanup;

    ret = krb5_copy_keyblock_contents(context, kd.key, key);
    if (ret)
	goto cleanup;

cleanup:
    krb5_data_free(&saltp);
    _krb5_free_key_data(context, &kd, et);

    return ret;
}
Пример #12
0
static krb5_error_code
kcm_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
{
    krb5_error_code ret;
    krb5_kcm_cursor c;
    krb5_storage *request, *response;
    krb5_data response_data;

    *cursor = NULL;

    c = calloc(1, sizeof(*c));
    if (c == NULL) {
	ret = krb5_enomem(context);
	goto out;
    }

    ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_UUID_LIST, &request);
    if (ret)
	goto out;

    ret = krb5_kcm_call(context, request, &response, &response_data);
    krb5_storage_free(request);
    if (ret)
	goto out;

    while (1) {
	ssize_t sret;
	kcmuuid_t uuid;
	void *ptr;

	sret = krb5_storage_read(response, &uuid, sizeof(uuid));
	if (sret == 0) {
	    ret = 0;
	    break;
	} else if (sret != sizeof(uuid)) {
	    ret = EINVAL;
	    goto out;
	}

	ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1));
	if (ptr == NULL) {
	    ret = krb5_enomem(context);
	    goto out;
	}
	c->uuids = ptr;

	memcpy(&c->uuids[c->length], &uuid, sizeof(uuid));
	c->length += 1;
    }

    krb5_storage_free(response);
    krb5_data_free(&response_data);

 out:
    if (ret && c) {
        free(c->uuids);
        free(c);
    } else
	*cursor = c;

    return ret;
}
Пример #13
0
static krb5_error_code
verify_logonname(krb5_context context,
		 const struct PAC_INFO_BUFFER *logon_name,
		 const krb5_data *data,
		 time_t authtime,
		 krb5_const_principal principal)
{
    krb5_error_code ret;
    krb5_principal p2;
    uint32_t time1, time2;
    krb5_storage *sp;
    uint16_t len;
    char *s;

    sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset_lo,
					logon_name->buffersize);
    if (sp == NULL)
	return krb5_enomem(context);

    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);

    CHECK(ret, krb5_ret_uint32(sp, &time1), out);
    CHECK(ret, krb5_ret_uint32(sp, &time2), out);

    {
	uint64_t t1, t2;
	t1 = unix2nttime(authtime);
	t2 = ((uint64_t)time2 << 32) | time1;
	if (t1 != t2) {
	    krb5_storage_free(sp);
	    krb5_set_error_message(context, EINVAL, "PAC timestamp mismatch");
	    return EINVAL;
	}
    }
    CHECK(ret, krb5_ret_uint16(sp, &len), out);
    if (len == 0) {
	krb5_storage_free(sp);
	krb5_set_error_message(context, EINVAL, "PAC logon name length missing");
	return EINVAL;
    }

    s = malloc(len);
    if (s == NULL) {
	krb5_storage_free(sp);
	return krb5_enomem(context);
    }
    ret = krb5_storage_read(sp, s, len);
    if (ret != len) {
	krb5_storage_free(sp);
	krb5_set_error_message(context, EINVAL, "Failed to read PAC logon name");
	return EINVAL;
    }
    krb5_storage_free(sp);
    {
	size_t ucs2len = len / 2;
	uint16_t *ucs2;
	size_t u8len;
	unsigned int flags = WIND_RW_LE;

	ucs2 = malloc(sizeof(ucs2[0]) * ucs2len);
	if (ucs2 == NULL)
	    return krb5_enomem(context);

	ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len);
	free(s);
	if (ret) {
	    free(ucs2);
	    krb5_set_error_message(context, ret, "Failed to convert string to UCS-2");
	    return ret;
	}
	ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len);
	if (ret) {
	    free(ucs2);
	    krb5_set_error_message(context, ret, "Failed to count length of UCS-2 string");
	    return ret;
	}
	u8len += 1; /* Add space for NUL */
	s = malloc(u8len);
	if (s == NULL) {
	    free(ucs2);
	    return krb5_enomem(context);
	}
	ret = wind_ucs2utf8(ucs2, ucs2len, s, &u8len);
	free(ucs2);
	if (ret) {
	    free(s);
	    krb5_set_error_message(context, ret, "Failed to convert to UTF-8");
	    return ret;
	}
    }
    ret = krb5_parse_name_flags(context, s, KRB5_PRINCIPAL_PARSE_NO_REALM, &p2);
    free(s);
    if (ret)
	return ret;

    if (krb5_principal_compare_any_realm(context, principal, p2) != TRUE) {
	ret = EINVAL;
	krb5_set_error_message(context, ret, "PAC logon name mismatch");
    }
    krb5_free_principal(context, p2);
    return ret;
out:
    return ret;
}
Пример #14
0
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_verify_ap_req2(krb5_context context,
		    krb5_auth_context *auth_context,
		    krb5_ap_req *ap_req,
		    krb5_const_principal server,
		    krb5_keyblock *keyblock,
		    krb5_flags flags,
		    krb5_flags *ap_req_options,
		    krb5_ticket **ticket,
		    krb5_key_usage usage)
{
    krb5_ticket *t;
    krb5_auth_context ac;
    krb5_error_code ret;
    EtypeList etypes;

    memset(&etypes, 0, sizeof(etypes));

    if (ticket)
	*ticket = NULL;

    if (auth_context && *auth_context) {
	ac = *auth_context;
    } else {
	ret = krb5_auth_con_init (context, &ac);
	if (ret)
	    return ret;
    }

    t = calloc(1, sizeof(*t));
    if (t == NULL) {
	ret = krb5_enomem(context);
	goto out;
    }

    if (ap_req->ap_options.use_session_key && ac->keyblock){
	ret = krb5_decrypt_ticket(context, &ap_req->ticket,
				  ac->keyblock,
				  &t->ticket,
				  flags);
	krb5_free_keyblock(context, ac->keyblock);
	ac->keyblock = NULL;
    }else
	ret = krb5_decrypt_ticket(context, &ap_req->ticket,
				  keyblock,
				  &t->ticket,
				  flags);

    if(ret)
	goto out;

    ret = _krb5_principalname2krb5_principal(context,
					     &t->server,
					     ap_req->ticket.sname,
					     ap_req->ticket.realm);
    if (ret) goto out;
    ret = _krb5_principalname2krb5_principal(context,
					     &t->client,
					     t->ticket.cname,
					     t->ticket.crealm);
    if (ret) goto out;

    ret = decrypt_authenticator (context,
				 &t->ticket.key,
				 &ap_req->authenticator,
				 ac->authenticator,
				 usage);
    if (ret)
	goto out;

    {
	krb5_principal p1, p2;
	krb5_boolean res;

	_krb5_principalname2krb5_principal(context,
					   &p1,
					   ac->authenticator->cname,
					   ac->authenticator->crealm);
	_krb5_principalname2krb5_principal(context,
					   &p2,
					   t->ticket.cname,
					   t->ticket.crealm);
	res = krb5_principal_compare (context, p1, p2);
	krb5_free_principal (context, p1);
	krb5_free_principal (context, p2);
	if (!res) {
	    ret = KRB5KRB_AP_ERR_BADMATCH;
	    krb5_clear_error_message (context);
	    goto out;
	}
    }

    /* check addresses */

    if (t->ticket.caddr
	&& ac->remote_address
	&& !krb5_address_search (context,
				 ac->remote_address,
				 t->ticket.caddr)) {
	ret = KRB5KRB_AP_ERR_BADADDR;
	krb5_clear_error_message (context);
	goto out;
    }

    /* check timestamp in authenticator */
    {
	krb5_timestamp now;

	krb5_timeofday (context, &now);

	if (labs(ac->authenticator->ctime - now) > context->max_skew) {
	    ret = KRB5KRB_AP_ERR_SKEW;
	    krb5_clear_error_message (context);
	    goto out;
	}
    }

    if (ac->authenticator->seq_number)
	krb5_auth_con_setremoteseqnumber(context, ac,
					 *ac->authenticator->seq_number);

    /* XXX - Xor sequence numbers */

    if (ac->authenticator->subkey) {
	ret = krb5_auth_con_setremotesubkey(context, ac,
					    ac->authenticator->subkey);
	if (ret)
	    goto out;
    }

    ret = find_etypelist(context, ac, &etypes);
    if (ret)
	goto out;

    ac->keytype = ETYPE_NULL;

    if (etypes.val) {
	size_t i;

	for (i = 0; i < etypes.len; i++) {
	    if (krb5_enctype_valid(context, etypes.val[i]) == 0) {
		ac->keytype = etypes.val[i];
		break;
	    }
	}
    }

    /* save key */
    ret = krb5_copy_keyblock(context, &t->ticket.key, &ac->keyblock);
    if (ret) goto out;

    if (ap_req_options) {
	*ap_req_options = 0;
	if (ac->keytype != (krb5_enctype)ETYPE_NULL)
	    *ap_req_options |= AP_OPTS_USE_SUBKEY;
	if (ap_req->ap_options.use_session_key)
	    *ap_req_options |= AP_OPTS_USE_SESSION_KEY;
	if (ap_req->ap_options.mutual_required)
	    *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
    }

    if(ticket)
	*ticket = t;
    else
	krb5_free_ticket (context, t);
    if (auth_context) {
	if (*auth_context == NULL)
	    *auth_context = ac;
    } else
	krb5_auth_con_free (context, ac);
    free_EtypeList(&etypes);
    return 0;
 out:
    free_EtypeList(&etypes);
    if (t)
	krb5_free_ticket (context, t);
    if (auth_context == NULL || *auth_context == NULL)
	krb5_auth_con_free (context, ac);
    return ret;
}
Пример #15
0
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_pac_add_buffer(krb5_context context, krb5_pac p,
		    uint32_t type, const krb5_data *data)
{
    krb5_error_code ret;
    void *ptr;
    size_t len, offset, header_end, old_end;
    uint32_t i;

    len = p->pac->numbuffers;

    ptr = realloc(p->pac,
		  sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * len));
    if (ptr == NULL)
	return krb5_enomem(context);

    p->pac = ptr;

    for (i = 0; i < len; i++)
	p->pac->buffers[i].offset_lo += PAC_INFO_BUFFER_SIZE;

    offset = p->data.length + PAC_INFO_BUFFER_SIZE;

    p->pac->buffers[len].type = type;
    p->pac->buffers[len].buffersize = data->length;
    p->pac->buffers[len].offset_lo = offset;
    p->pac->buffers[len].offset_hi = 0;

    old_end = p->data.length;
    len = p->data.length + data->length + PAC_INFO_BUFFER_SIZE;
    if (len < p->data.length) {
	krb5_set_error_message(context, EINVAL, "integer overrun");
	return EINVAL;
    }

    /* align to PAC_ALIGNMENT */
    len = ((len + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT;

    ret = krb5_data_realloc(&p->data, len);
    if (ret) {
	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
	return ret;
    }

    /*
     * make place for new PAC INFO BUFFER header
     */
    header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
    memmove((unsigned char *)p->data.data + header_end + PAC_INFO_BUFFER_SIZE,
	    (unsigned char *)p->data.data + header_end ,
	    old_end - header_end);
    memset((unsigned char *)p->data.data + header_end, 0, PAC_INFO_BUFFER_SIZE);

    /*
     * copy in new data part
     */

    memcpy((unsigned char *)p->data.data + offset,
	   data->data, data->length);
    memset((unsigned char *)p->data.data + offset + data->length,
	   0, p->data.length - offset - data->length);

    p->pac->numbuffers += 1;

    return 0;
}
Пример #16
0
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_rd_req_ctx(krb5_context context,
		krb5_auth_context *auth_context,
		const krb5_data *inbuf,
		krb5_const_principal server,
		krb5_rd_req_in_ctx inctx,
		krb5_rd_req_out_ctx *outctx)
{
    krb5_error_code ret;
    krb5_ap_req ap_req;
    krb5_rd_req_out_ctx o = NULL;
    krb5_keytab id = NULL, keytab = NULL;
    krb5_principal service = NULL;

    *outctx = NULL;

    o = calloc(1, sizeof(*o));
    if (o == NULL)
	return krb5_enomem(context);

    if (*auth_context == NULL) {
	ret = krb5_auth_con_init(context, auth_context);
	if (ret)
	    goto out;
    }

    ret = krb5_decode_ap_req(context, inbuf, &ap_req);
    if(ret)
	goto out;

    /* Save the principal that was in the request */
    ret = _krb5_principalname2krb5_principal(context,
					     &o->server,
					     ap_req.ticket.sname,
					     ap_req.ticket.realm);
    if (ret)
	goto out;

    if (ap_req.ap_options.use_session_key &&
	(*auth_context)->keyblock == NULL) {
	ret = KRB5KRB_AP_ERR_NOKEY;
	krb5_set_error_message(context, ret,
			       N_("krb5_rd_req: user to user auth "
				  "without session key given", ""));
	goto out;
    }

    if (inctx && inctx->keytab)
	id = inctx->keytab;

    if((*auth_context)->keyblock){
	ret = krb5_copy_keyblock(context,
				 (*auth_context)->keyblock,
				 &o->keyblock);
	if (ret)
	    goto out;
    } else if(inctx && inctx->keyblock){
	ret = krb5_copy_keyblock(context,
				 inctx->keyblock,
				 &o->keyblock);
	if (ret)
	    goto out;
    } else {

	if(id == NULL) {
	    krb5_kt_default(context, &keytab);
	    id = keytab;
	}
	if (id == NULL)
	    goto out;

	if (server == NULL) {
	    ret = _krb5_principalname2krb5_principal(context,
						     &service,
						     ap_req.ticket.sname,
						     ap_req.ticket.realm);
	    if (ret)
		goto out;
	    server = service;
	}

	ret = get_key_from_keytab(context,
				  &ap_req,
				  server,
				  id,
				  &o->keyblock);
	if (ret) {
	    /* If caller specified a server, fail. */
	    if (service == NULL && (context->flags & KRB5_CTX_F_RD_REQ_IGNORE) == 0)
		goto out;
	    /* Otherwise, fall back to iterating over the keytab. This
	     * have serious performace issues for larger keytab.
	     */
	    o->keyblock = NULL;
	}
    }

    if (o->keyblock) {
	/*
	 * We got an exact keymatch, use that.
	 */

	ret = krb5_verify_ap_req2(context,
				  auth_context,
				  &ap_req,
				  server,
				  o->keyblock,
				  0,
				  &o->ap_req_options,
				  &o->ticket,
				  KRB5_KU_AP_REQ_AUTH);

	if (ret)
	    goto out;

    } else {
	/*
	 * Interate over keytab to find a key that can decrypt the request.
	 */

	krb5_keytab_entry entry;
	krb5_kt_cursor cursor;
	int done = 0, kvno = 0;

	memset(&cursor, 0, sizeof(cursor));

	if (ap_req.ticket.enc_part.kvno)
	    kvno = *ap_req.ticket.enc_part.kvno;

	ret = krb5_kt_start_seq_get(context, id, &cursor);
	if (ret)
	    goto out;

	done = 0;
	while (!done) {
	    krb5_principal p;

	    ret = krb5_kt_next_entry(context, id, &entry, &cursor);
	    if (ret) {
		_krb5_kt_principal_not_found(context, ret, id, o->server,
					     ap_req.ticket.enc_part.etype,
					     kvno);
		break;
	    }

	    if (entry.keyblock.keytype != ap_req.ticket.enc_part.etype) {
		krb5_kt_free_entry (context, &entry);
		continue;
	    }

	    ret = krb5_verify_ap_req2(context,
				      auth_context,
				      &ap_req,
				      server,
				      &entry.keyblock,
				      0,
				      &o->ap_req_options,
				      &o->ticket,
				      KRB5_KU_AP_REQ_AUTH);
	    if (ret) {
		krb5_kt_free_entry (context, &entry);
		continue;
	    }

	    /*
	     * Found a match, save the keyblock for PAC processing,
	     * and update the service principal in the ticket to match
	     * whatever is in the keytab.
	     */

	    ret = krb5_copy_keyblock(context,
				     &entry.keyblock,
				     &o->keyblock);
	    if (ret) {
		krb5_kt_free_entry (context, &entry);
		break;
	    }

	    ret = krb5_copy_principal(context, entry.principal, &p);
	    if (ret) {
		krb5_kt_free_entry (context, &entry);
		break;
	    }
	    krb5_free_principal(context, o->ticket->server);
	    o->ticket->server = p;

	    krb5_kt_free_entry (context, &entry);

	    done = 1;
	}
	krb5_kt_end_seq_get (context, id, &cursor);
        if (ret)
            goto out;
    }

    /* If there is a PAC, verify its server signature */
    if (inctx == NULL || inctx->check_pac) {
	krb5_pac pac;
	krb5_data data;

	ret = krb5_ticket_get_authorization_data_type(context,
						      o->ticket,
						      KRB5_AUTHDATA_WIN2K_PAC,
						      &data);
	if (ret == 0) {
	    ret = krb5_pac_parse(context, data.data, data.length, &pac);
	    krb5_data_free(&data);
	    if (ret)
		goto out;

	    ret = krb5_pac_verify(context,
				  pac,
				  o->ticket->ticket.authtime,
				  o->ticket->client,
				  o->keyblock,
				  NULL);
	    krb5_pac_free(context, pac);
	    if (ret)
		goto out;
	} else
	  ret = 0;
    }
out:

    if (ret || outctx == NULL) {
	krb5_rd_req_out_ctx_free(context, o);
    } else
	*outctx = o;

    free_AP_REQ(&ap_req);

    if (service)
	krb5_free_principal(context, service);

    if (keytab)
	krb5_kt_close(context, keytab);

    return ret;
}
Пример #17
0
static krb5_error_code
verify_checksum(krb5_context context,
		const struct PAC_INFO_BUFFER *sig,
		const krb5_data *data,
		void *ptr, size_t len,
		const krb5_keyblock *key)
{
    krb5_storage *sp = NULL;
    uint32_t type;
    krb5_error_code ret;
    Checksum cksum;

    memset(&cksum, 0, sizeof(cksum));

    sp = krb5_storage_from_mem((char *)data->data + sig->offset_lo,
			       sig->buffersize);
    if (sp == NULL)
	return krb5_enomem(context);

    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);

    CHECK(ret, krb5_ret_uint32(sp, &type), out);
    cksum.cksumtype = type;
    cksum.checksum.length =
	sig->buffersize - krb5_storage_seek(sp, 0, SEEK_CUR);
    cksum.checksum.data = malloc(cksum.checksum.length);
    if (cksum.checksum.data == NULL) {
	ret = krb5_enomem(context);
	goto out;
    }
    ret = krb5_storage_read(sp, cksum.checksum.data, cksum.checksum.length);
    if (ret != (int)cksum.checksum.length) {
	ret = EINVAL;
	krb5_set_error_message(context, ret, "PAC checksum missing checksum");
	goto out;
    }

    if (!krb5_checksum_is_keyed(context, cksum.cksumtype)) {
	ret = EINVAL;
	krb5_set_error_message(context, ret, "Checksum type %d not keyed",
			       cksum.cksumtype);
	goto out;
    }

    /* If the checksum is HMAC-MD5, the checksum type is not tied to
     * the key type, instead the HMAC-MD5 checksum is applied blindly
     * on whatever key is used for this connection, avoiding issues
     * with unkeyed checksums on des-cbc-md5 and des-cbc-crc.  See
     * http://comments.gmane.org/gmane.comp.encryption.kerberos.devel/8743
     * for the same issue in MIT, and
     * http://blogs.msdn.com/b/openspecification/archive/2010/01/01/verifying-the-server-signature-in-kerberos-privilege-account-certificate.aspx
     * for Microsoft's explaination */

    if (cksum.cksumtype == CKSUMTYPE_HMAC_MD5) {
	Checksum local_checksum;

	memset(&local_checksum, 0, sizeof(local_checksum));

	ret = HMAC_MD5_any_checksum(context, key, ptr, len,
				    KRB5_KU_OTHER_CKSUM, &local_checksum);

	if (ret != 0 || krb5_data_ct_cmp(&local_checksum.checksum, &cksum.checksum) != 0) {
	    ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
	    krb5_set_error_message(context, ret,
				   N_("PAC integrity check failed for "
				      "hmac-md5 checksum", ""));
	}
	krb5_data_free(&local_checksum.checksum);

   } else {
	krb5_crypto crypto = NULL;

	ret = krb5_crypto_init(context, key, 0, &crypto);
	if (ret)
		goto out;

	ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM,
				   ptr, len, &cksum);
	krb5_crypto_destroy(context, crypto);
    }
    free(cksum.checksum.data);
    krb5_storage_free(sp);

    return ret;

out:
    if (cksum.checksum.data)
	free(cksum.checksum.data);
    if (sp)
	krb5_storage_free(sp);
    return ret;
}
Пример #18
0
static krb5_error_code KRB5_CALLCONV
xcc_store_cred(krb5_context context,
	       krb5_ccache id,
	       krb5_creds *creds)
{
    krb5_xcc *x = XCACHE(id);
    krb5_storage *sp = NULL;
    CFDataRef dref = NULL;
    krb5_data data;
    CFStringRef principal = NULL;
    CFDictionaryRef query = NULL;
    krb5_error_code ret;
    CFBooleanRef is_tgt = kCFBooleanFalse;
    CFDateRef authtime = NULL;
    
    krb5_data_zero(&data);
    
    if (creds->times.starttime) {
	authtime = CFDateCreate(NULL, (CFTimeInterval)creds->times.starttime - kCFAbsoluteTimeIntervalSince1970);
    } else if (creds->times.authtime) {
	authtime = CFDateCreate(NULL, (CFTimeInterval)creds->times.authtime - kCFAbsoluteTimeIntervalSince1970);
    } else {
	authtime = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
    }
    if (authtime == NULL) {
	ret = krb5_enomem(context);
	goto out;
    }

    sp = krb5_storage_emem();
    if (sp == NULL) {
	ret = krb5_enomem(context);
	goto out;
    }
    
    ret = krb5_store_creds(sp, creds);
    if (ret)
	goto out;
    
    krb5_storage_to_data(sp, &data);
    
    dref = CFDataCreateWithBytesNoCopy(NULL, data.data, data.length, kCFAllocatorNull);
    if (dref == NULL) {
	ret = krb5_enomem(context);
	goto out;
    }
    
    if (krb5_principal_is_root_krbtgt(context, creds->server))
	is_tgt = kCFBooleanTrue;

    principal = CFStringCreateFromPrincipal(context, creds->server);
    if (principal == NULL) {
	ret = krb5_enomem(context);
	goto out;
    }
    
    const void *add_keys[] = {
	(void *)kHEIMObjectType,
	kHEIMAttrType,
	kHEIMAttrClientName,
	kHEIMAttrServerName,
	kHEIMAttrData,
	kHEIMAttrParentCredential,
	kHEIMAttrLeadCredential,
	kHEIMAttrAuthTime,
    };
    const void *add_values[] = {
	(void *)kHEIMObjectKerberos,
	kHEIMTypeKerberos,
	x->clientName,
	principal,
	dref,
	x->uuid,
	is_tgt,
	authtime,
    };
    
    query = CFDictionaryCreate(NULL, add_keys, add_values, sizeof(add_keys) / sizeof(add_keys[0]), NULL, NULL);
    heim_assert(query != NULL, "Failed to create dictionary");
    
    HeimCredRef ccred = HeimCredCreate(query, NULL);
    if (ccred) {
	CFRelease(ccred);
    } else {
	_krb5_debugx(context, 5, "failed to add credential to %s\n", x->cache_name);
	ret = EINVAL;
	krb5_set_error_message(context, ret, "failed to store credential to %s", x->cache_name);
	goto out;
    }
    
out:
    if (sp)
	krb5_storage_free(sp);
    CFRELEASE_NULL(query);
    CFRELEASE_NULL(dref);
    CFRELEASE_NULL(principal);
    CFRELEASE_NULL(authtime);
    krb5_data_free(&data);
    
    return ret;
}
Пример #19
0
static krb5_error_code
verify_logonname(krb5_context context,
		 const struct PAC_INFO_BUFFER *logon_name,
		 const krb5_data *data,
		 time_t authtime,
		 krb5_const_principal principal)
{
    krb5_error_code ret;
    uint32_t time1, time2;
    krb5_storage *sp;
    uint16_t len;
    char *s = NULL;
    char *principal_string = NULL;
    char *logon_string = NULL;

    sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset_lo,
					logon_name->buffersize);
    if (sp == NULL)
	return krb5_enomem(context);

    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);

    CHECK(ret, krb5_ret_uint32(sp, &time1), out);
    CHECK(ret, krb5_ret_uint32(sp, &time2), out);

    {
	uint64_t t1, t2;
	t1 = unix2nttime(authtime);
	t2 = ((uint64_t)time2 << 32) | time1;
	/*
	 * When neither the ticket nor the PAC set an explicit authtime,
	 * both times are zero, but relative to different time scales.
	 * So we must compare "not set" values without converting to a
	 * common time reference.
         */
	if (t1 != t2 && (t2 != 0 && authtime != 0)) {
	    krb5_storage_free(sp);
	    krb5_set_error_message(context, EINVAL, "PAC timestamp mismatch");
	    return EINVAL;
	}
    }
    CHECK(ret, krb5_ret_uint16(sp, &len), out);
    if (len == 0) {
	krb5_storage_free(sp);
	krb5_set_error_message(context, EINVAL, "PAC logon name length missing");
	return EINVAL;
    }

    s = malloc(len);
    if (s == NULL) {
	krb5_storage_free(sp);
	return krb5_enomem(context);
    }
    ret = krb5_storage_read(sp, s, len);
    if (ret != len) {
	krb5_storage_free(sp);
	krb5_set_error_message(context, EINVAL, "Failed to read PAC logon name");
	return EINVAL;
    }
    krb5_storage_free(sp);
    {
	size_t ucs2len = len / 2;
	uint16_t *ucs2;
	size_t u8len;
	unsigned int flags = WIND_RW_LE;

	ucs2 = malloc(sizeof(ucs2[0]) * ucs2len);
	if (ucs2 == NULL)
	    return krb5_enomem(context);

	ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len);
	free(s);
	if (ret) {
	    free(ucs2);
	    krb5_set_error_message(context, ret, "Failed to convert string to UCS-2");
	    return ret;
	}
	ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len);
	if (ret) {
	    free(ucs2);
	    krb5_set_error_message(context, ret, "Failed to count length of UCS-2 string");
	    return ret;
	}
	u8len += 1; /* Add space for NUL */
	logon_string = malloc(u8len);
	if (logon_string == NULL) {
	    free(ucs2);
	    return krb5_enomem(context);
	}
	ret = wind_ucs2utf8(ucs2, ucs2len, logon_string, &u8len);
	free(ucs2);
	if (ret) {
	    free(logon_string);
	    krb5_set_error_message(context, ret, "Failed to convert to UTF-8");
	    return ret;
	}
    }
    ret = krb5_unparse_name_flags(context, principal,
				  KRB5_PRINCIPAL_UNPARSE_NO_REALM |
				  KRB5_PRINCIPAL_UNPARSE_DISPLAY,
				  &principal_string);
    if (ret) {
	free(logon_string);
	return ret;
    }

    ret = strcmp(logon_string, principal_string);
    if (ret != 0) {
	ret = EINVAL;
	krb5_set_error_message(context, ret, "PAC logon name [%s] mismatch principal name [%s]",
			       logon_string, principal_string);
    }
    free(logon_string);
    free(principal_string);
    return ret;
out:
    return ret;
}
Пример #20
0
static krb5_error_code
parse_key_set(krb5_context context, const char *key,
	      krb5_enctype **ret_enctypes, size_t *ret_num_enctypes,
	      krb5_salt *salt, krb5_principal principal)
{
    const char *p;
    char buf[3][256];
    int num_buf = 0;
    int i, num_enctypes = 0;
    krb5_enctype e;
    const krb5_enctype *enctypes = NULL;
    krb5_error_code ret;

    p = key;

    *ret_enctypes = NULL;
    *ret_num_enctypes = 0;

    /* split p in a list of :-separated strings */
    for(num_buf = 0; num_buf < 3; num_buf++)
	if(strsep_copy(&p, ":", buf[num_buf], sizeof(buf[num_buf])) == -1)
	    break;

    salt->saltvalue.data = NULL;
    salt->saltvalue.length = 0;

    for(i = 0; i < num_buf; i++) {
	if(enctypes == NULL && num_buf > 1) {
	    /* this might be a etype specifier */
	    /* XXX there should be a string_to_etypes handling
	       special cases like `des' and `all' */
	    if(strcmp(buf[i], "des") == 0) {
		enctypes = des_etypes;
		num_enctypes = sizeof(des_etypes)/sizeof(des_etypes[0]);
	    } else if(strcmp(buf[i], "des3") == 0) {
		e = KRB5_ENCTYPE_DES3_CBC_SHA1;
		enctypes = &e;
		num_enctypes = 1;
	    } else {
		ret = krb5_string_to_enctype(context, buf[i], &e);
		if (ret == 0) {
		    enctypes = &e;
		    num_enctypes = 1;
		} else
		    return ret;
	    }
	    continue;
	}
	if(salt->salttype == 0) {
	    /* interpret string as a salt specifier, if no etype
	       is set, this sets default values */
	    /* XXX should perhaps use string_to_salttype, but that
	       interface sucks */
	    if(strcmp(buf[i], "pw-salt") == 0) {
		if(enctypes == NULL) {
		    enctypes = all_etypes;
		    num_enctypes = sizeof(all_etypes)/sizeof(all_etypes[0]);
		}
		salt->salttype = KRB5_PW_SALT;
	    } else if(strcmp(buf[i], "afs3-salt") == 0) {
		if(enctypes == NULL) {
		    enctypes = des_etypes;
		    num_enctypes = sizeof(des_etypes)/sizeof(des_etypes[0]);
		}
		salt->salttype = KRB5_AFS3_SALT;
	    }
	    continue;
	}

        if (salt->saltvalue.data != NULL)
            free(salt->saltvalue.data);
        /* if there is a final string, use it as the string to
           salt with, this is mostly useful with null salt for
           v4 compat, and a cell name for afs compat */
        salt->saltvalue.data = strdup(buf[i]);
        if (salt->saltvalue.data == NULL)
            return krb5_enomem(context);
        salt->saltvalue.length = strlen(buf[i]);
    }

    if(enctypes == NULL || salt->salttype == 0) {
	krb5_free_salt(context, *salt);
	krb5_set_error_message(context, EINVAL, "bad value for default_keys `%s'", key);
	return EINVAL;
    }

    /* if no salt was specified make up default salt */
    if(salt->saltvalue.data == NULL) {
	if(salt->salttype == KRB5_PW_SALT) {
	    ret = krb5_get_pw_salt(context, principal, salt);
	    if (ret)
		return ret;
	} else if(salt->salttype == KRB5_AFS3_SALT) {
	    krb5_const_realm realm = krb5_principal_get_realm(context, principal);
	    salt->saltvalue.data = strdup(realm);
	    if(salt->saltvalue.data == NULL) {
		krb5_set_error_message(context, ENOMEM,
				       "out of memory while "
				       "parsing salt specifiers");
		return ENOMEM;
	    }
	    strlwr(salt->saltvalue.data);
	    salt->saltvalue.length = strlen(realm);
	}
    }

    *ret_enctypes = malloc(sizeof(enctypes[0]) * num_enctypes);
    if (*ret_enctypes == NULL) {
	krb5_free_salt(context, *salt);
	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
	return ENOMEM;
    }
    memcpy(*ret_enctypes, enctypes, sizeof(enctypes[0]) * num_enctypes);
    *ret_num_enctypes = num_enctypes;

    return 0;
}
Пример #21
0
static int
do_ext_keytab(krb5_principal principal, void *data)
{
    krb5_error_code ret;
    kadm5_principal_ent_rec princ;
    struct ext_keytab_data *e = data;
    krb5_keytab_entry *keys = NULL;
    krb5_keyblock *k = NULL;
    size_t i;
    int n_k = 0;
    uint32_t mask;
    char *unparsed = NULL;

    mask = KADM5_PRINCIPAL;
    if (!e->random_key_flag)
        mask |= KADM5_KVNO | KADM5_KEY_DATA;

    ret = kadm5_get_principal(kadm_handle, principal, &princ, mask);
    if (ret)
	return ret;

    ret = krb5_unparse_name(context, principal, &unparsed);
    if (ret)
	goto out;

    if (!e->random_key_flag) {
        if (princ.n_key_data == 0) {
            krb5_warnx(context, "principal has no keys, or user lacks "
                       "get-keys privilege for %s", unparsed);
            goto out;
        }
        /*
         * kadmin clients and servers from master between 1.5 and 1.6
         * can have corrupted a principal's keys in the HDB.  If some
         * are bogus but not all are, then that must have happened.
         *
         * If all keys are bogus then the server may be a pre-1.6,
         * post-1.5 server and the client lacks get-keys privilege, or
         * the keys are corrupted.  We can't tell here.
         */
        if (kadm5_all_keys_are_bogus(princ.n_key_data, princ.key_data)) {
            krb5_warnx(context, "user lacks get-keys privilege for %s",
                       unparsed);
            goto out;
        }
        if (kadm5_some_keys_are_bogus(princ.n_key_data, princ.key_data)) {
            krb5_warnx(context, "some keys for %s are corrupted in the HDB",
                       unparsed);
        }
	keys = calloc(sizeof(*keys), princ.n_key_data);
	if (keys == NULL) {
	    ret = krb5_enomem(context);
	    goto out;
	}
	for (i = 0; i < princ.n_key_data; i++) {
	    krb5_key_data *kd = &princ.key_data[i];

            /* Don't extract bogus keys */
            if (kadm5_all_keys_are_bogus(1, kd))
                continue;

	    keys[i].principal = princ.principal;
	    keys[i].vno = kd->key_data_kvno;
	    keys[i].keyblock.keytype = kd->key_data_type[0];
	    keys[i].keyblock.keyvalue.length = kd->key_data_length[0];
	    keys[i].keyblock.keyvalue.data = kd->key_data_contents[0];
	    keys[i].timestamp = time(NULL);
            n_k++;
	}
    } else if (e->random_key_flag) {
	ret = kadm5_randkey_principal(kadm_handle, principal, &k, &n_k);
	if (ret)
	    goto out;

	keys = calloc(sizeof(*keys), n_k);
	if (keys == NULL) {
	    ret = krb5_enomem(context);
	    goto out;
	}
	for (i = 0; i < n_k; i++) {
	    keys[i].principal = principal;
	    keys[i].vno = princ.kvno + 1; /* XXX get entry again */
	    keys[i].keyblock = k[i];
	    keys[i].timestamp = time(NULL);
	}
    }

    if (n_k == 0)
        krb5_warn(context, ret, "no keys written to keytab for %s", unparsed);

    for (i = 0; i < n_k; i++) {
	ret = krb5_kt_add_entry(context, e->keytab, &keys[i]);
	if (ret)
	    krb5_warn(context, ret, "krb5_kt_add_entry(%lu)", (unsigned long)i);
    }

  out:
    kadm5_free_principal_ent(kadm_handle, &princ);
    if (k) {
        for (i = 0; i < n_k; i++)
            memset(k[i].keyvalue.data, 0, k[i].keyvalue.length);
	free(k);
    }
    free(unparsed);
    free(keys);
    return 0;
}