mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2024-12-26 00:41:25 -06:00
505bc85ec3
This moves the decoding function that reads the keys from the ber format into a structure in the common krb5 util code right below the function that encodes the same data structure into a ber format. This way the 2 functions are in the same place and can be both used by all ia components.
1045 lines
28 KiB
C
1045 lines
28 KiB
C
/*
|
|
* Kerberos related utils for FreeIPA
|
|
*
|
|
* Authors: Simo Sorce <ssorce@redhat.com>
|
|
*
|
|
* Copyright (C) 2011 Simo Sorce, Red Hat
|
|
* see file 'COPYING' for use and warranty information
|
|
*
|
|
* This program is free software you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <lber.h>
|
|
#include <errno.h>
|
|
|
|
#include <libintl.h>
|
|
#define _(STRING) gettext(STRING)
|
|
|
|
#include "ipa_krb5.h"
|
|
|
|
/* Salt types */
|
|
#define KRB5P_SALT_SIZE 16
|
|
|
|
static krb5_error_code ipa_get_random_salt(krb5_context krbctx,
|
|
krb5_data *salt)
|
|
{
|
|
krb5_error_code kerr;
|
|
int i, v;
|
|
|
|
/* make random salt */
|
|
salt->length = KRB5P_SALT_SIZE;
|
|
salt->data = malloc(KRB5P_SALT_SIZE);
|
|
if (!salt->data) {
|
|
return ENOMEM;
|
|
}
|
|
kerr = krb5_c_random_make_octets(krbctx, salt);
|
|
if (kerr) {
|
|
return kerr;
|
|
}
|
|
|
|
/* Windows treats the salt as a string.
|
|
* To avoid any compatibility issue, limits octects only to
|
|
* the ASCII printable range, or 0x20 <= val <= 0x7E */
|
|
for (i = 0; i < salt->length; i++) {
|
|
v = (unsigned char)salt->data[i];
|
|
v %= 0x5E; /* 7E - 20 */
|
|
v += 0x20; /* add base */
|
|
salt->data[i] = v;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
ipa_krb5_free_ktypes(krb5_context context, krb5_enctype *val)
|
|
{
|
|
free(val);
|
|
}
|
|
|
|
/*
|
|
* Convert a krb5_principal into the default salt for that principal.
|
|
*/
|
|
krb5_error_code ipa_krb5_principal2salt_norealm(krb5_context context,
|
|
krb5_const_principal pr,
|
|
krb5_data *ret)
|
|
{
|
|
unsigned int size = 0, offset=0;
|
|
krb5_int32 nelem;
|
|
register int i;
|
|
|
|
if (pr == NULL) {
|
|
ret->length = 0;
|
|
ret->data = NULL;
|
|
return 0;
|
|
}
|
|
|
|
nelem = krb5_princ_size(context, pr);
|
|
|
|
for (i = 0; i < (int) nelem; i++)
|
|
size += krb5_princ_component(context, pr, i)->length;
|
|
|
|
ret->length = size;
|
|
if (!(ret->data = malloc (size)))
|
|
return ENOMEM;
|
|
|
|
for (i = 0; i < (int) nelem; i++) {
|
|
memcpy(&ret->data[offset], krb5_princ_component(context, pr, i)->data,
|
|
krb5_princ_component(context, pr, i)->length);
|
|
offset += krb5_princ_component(context, pr, i)->length;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void krb5int_c_free_keyblock_contents(krb5_context context,
|
|
register krb5_keyblock *key);
|
|
|
|
/*
|
|
* Generate a krb5_key_data set by encrypting keys according to
|
|
* enctype/salttype preferences
|
|
*/
|
|
krb5_error_code ipa_krb5_generate_key_data(krb5_context krbctx,
|
|
krb5_principal principal,
|
|
krb5_data pwd, int kvno,
|
|
krb5_keyblock *kmkey,
|
|
int num_encsalts,
|
|
krb5_key_salt_tuple *encsalts,
|
|
int *_num_keys,
|
|
krb5_key_data **_keys)
|
|
{
|
|
krb5_error_code kerr;
|
|
krb5_key_data *keys;
|
|
int num_keys;
|
|
int i;
|
|
|
|
num_keys = num_encsalts;
|
|
keys = calloc(num_keys, sizeof(krb5_key_data));
|
|
if (!keys) {
|
|
return ENOMEM;
|
|
}
|
|
|
|
for (i = 0; i < num_keys; i++) {
|
|
krb5_keyblock key;
|
|
krb5_data salt;
|
|
krb5_octet *ptr;
|
|
krb5_data plain;
|
|
krb5_enc_data cipher;
|
|
krb5_int16 t;
|
|
size_t len;
|
|
|
|
salt.data = NULL;
|
|
|
|
keys[i].key_data_ver = 2; /* we always have a salt */
|
|
keys[i].key_data_kvno = kvno;
|
|
|
|
switch (encsalts[i].ks_salttype) {
|
|
|
|
case KRB5_KDB_SALTTYPE_ONLYREALM:
|
|
|
|
if (!principal->realm.data) {
|
|
kerr = EINVAL;
|
|
goto done;
|
|
}
|
|
salt.length = principal->realm.length;
|
|
salt.data = malloc(salt.length);
|
|
if (!salt.data) {
|
|
kerr = ENOMEM;
|
|
goto done;
|
|
}
|
|
memcpy(salt.data, principal->realm.data, salt.length);
|
|
break;
|
|
|
|
case KRB5_KDB_SALTTYPE_NOREALM:
|
|
|
|
kerr = ipa_krb5_principal2salt_norealm(krbctx, principal, &salt);
|
|
if (kerr) {
|
|
goto done;
|
|
}
|
|
break;
|
|
|
|
case KRB5_KDB_SALTTYPE_NORMAL:
|
|
|
|
kerr = krb5_principal2salt(krbctx, principal, &salt);
|
|
if (kerr) {
|
|
goto done;
|
|
}
|
|
break;
|
|
|
|
case KRB5_KDB_SALTTYPE_SPECIAL:
|
|
|
|
kerr = ipa_get_random_salt(krbctx, &salt);
|
|
if (kerr) {
|
|
goto done;
|
|
}
|
|
break;
|
|
|
|
case KRB5_KDB_SALTTYPE_V4:
|
|
salt.length = 0;
|
|
break;
|
|
|
|
case KRB5_KDB_SALTTYPE_AFS3:
|
|
|
|
if (!principal->realm.data) {
|
|
kerr = EINVAL;
|
|
goto done;
|
|
}
|
|
salt.data = strndup((char *)principal->realm.data,
|
|
principal->realm.length);
|
|
if (!salt.data) {
|
|
kerr = ENOMEM;
|
|
goto done;
|
|
}
|
|
salt.length = SALT_TYPE_AFS_LENGTH; /* special value */
|
|
break;
|
|
|
|
default:
|
|
kerr = EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
/* need to build the key now to manage the AFS salt.length
|
|
* special case */
|
|
kerr = krb5_c_string_to_key(krbctx,
|
|
encsalts[i].ks_enctype,
|
|
&pwd, &salt, &key);
|
|
if (kerr) {
|
|
krb5_free_data_contents(krbctx, &salt);
|
|
goto done;
|
|
}
|
|
if (salt.length == SALT_TYPE_AFS_LENGTH) {
|
|
salt.length = strlen(salt.data);
|
|
}
|
|
|
|
kerr = krb5_c_encrypt_length(krbctx,
|
|
kmkey->enctype, key.length, &len);
|
|
if (kerr) {
|
|
krb5int_c_free_keyblock_contents(krbctx, &key);
|
|
krb5_free_data_contents(krbctx, &salt);
|
|
goto done;
|
|
}
|
|
|
|
if ((ptr = (krb5_octet *) malloc(2 + len)) == NULL) {
|
|
kerr = ENOMEM;
|
|
krb5int_c_free_keyblock_contents(krbctx, &key);
|
|
krb5_free_data_contents(krbctx, &salt);
|
|
goto done;
|
|
}
|
|
|
|
t = htole16(key.length);
|
|
memcpy(ptr, &t, 2);
|
|
|
|
plain.length = key.length;
|
|
plain.data = (char *)key.contents;
|
|
|
|
cipher.ciphertext.length = len;
|
|
cipher.ciphertext.data = (char *)ptr+2;
|
|
|
|
kerr = krb5_c_encrypt(krbctx, kmkey, 0, 0, &plain, &cipher);
|
|
if (kerr) {
|
|
krb5int_c_free_keyblock_contents(krbctx, &key);
|
|
krb5_free_data_contents(krbctx, &salt);
|
|
free(ptr);
|
|
goto done;
|
|
}
|
|
|
|
/* KrbSalt */
|
|
keys[i].key_data_type[1] = encsalts[i].ks_salttype;
|
|
|
|
if (salt.length) {
|
|
keys[i].key_data_length[1] = salt.length;
|
|
keys[i].key_data_contents[1] = (krb5_octet *)salt.data;
|
|
}
|
|
|
|
/* EncryptionKey */
|
|
keys[i].key_data_type[0] = key.enctype;
|
|
keys[i].key_data_length[0] = len + 2;
|
|
keys[i].key_data_contents[0] = malloc(len + 2);
|
|
if (!keys[i].key_data_contents[0]) {
|
|
kerr = ENOMEM;
|
|
krb5int_c_free_keyblock_contents(krbctx, &key);
|
|
free(ptr);
|
|
goto done;
|
|
}
|
|
memcpy(keys[i].key_data_contents[0], ptr, len + 2);
|
|
|
|
/* make sure we free the memory used now that we are done with it */
|
|
krb5int_c_free_keyblock_contents(krbctx, &key);
|
|
free(ptr);
|
|
}
|
|
|
|
*_num_keys = num_keys;
|
|
*_keys = keys;
|
|
kerr = 0;
|
|
|
|
done:
|
|
if (kerr) {
|
|
ipa_krb5_free_key_data(keys, num_keys);
|
|
}
|
|
|
|
return kerr;
|
|
}
|
|
|
|
void ipa_krb5_free_key_data(krb5_key_data *keys, int num_keys)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < num_keys; i++) {
|
|
/* try to wipe key from memory,
|
|
* hopefully the compiler will not optimize it away */
|
|
if (keys[i].key_data_length[0]) {
|
|
memset(keys[i].key_data_contents[0],
|
|
0, keys[i].key_data_length[0]);
|
|
}
|
|
free(keys[i].key_data_contents[0]);
|
|
free(keys[i].key_data_contents[1]);
|
|
}
|
|
free(keys);
|
|
}
|
|
|
|
/* Novell key-format scheme:
|
|
|
|
KrbKeySet ::= SEQUENCE {
|
|
attribute-major-vno [0] UInt16,
|
|
attribute-minor-vno [1] UInt16,
|
|
kvno [2] UInt32,
|
|
mkvno [3] UInt32 OPTIONAL,
|
|
keys [4] SEQUENCE OF KrbKey,
|
|
...
|
|
}
|
|
|
|
KrbKey ::= SEQUENCE {
|
|
salt [0] KrbSalt OPTIONAL,
|
|
key [1] EncryptionKey,
|
|
s2kparams [2] OCTET STRING OPTIONAL,
|
|
...
|
|
}
|
|
|
|
KrbSalt ::= SEQUENCE {
|
|
type [0] Int32,
|
|
salt [1] OCTET STRING OPTIONAL
|
|
}
|
|
|
|
EncryptionKey ::= SEQUENCE {
|
|
keytype [0] Int32,
|
|
keyvalue [1] OCTET STRING
|
|
}
|
|
|
|
*/
|
|
|
|
int ber_encode_krb5_key_data(krb5_key_data *data,
|
|
int numk, int mkvno,
|
|
struct berval **encoded)
|
|
{
|
|
BerElement *be = NULL;
|
|
ber_tag_t tag;
|
|
int ret, i;
|
|
|
|
be = ber_alloc_t(LBER_USE_DER);
|
|
if (!be) {
|
|
return ENOMEM;
|
|
}
|
|
|
|
tag = LBER_CONSTRUCTED | LBER_CLASS_CONTEXT;
|
|
|
|
ret = ber_printf(be, "{t[i]t[i]t[i]t[i]t[{",
|
|
tag | 0, 1, tag | 1, 1,
|
|
tag | 2, (ber_int_t)data[0].key_data_kvno,
|
|
tag | 3, (ber_int_t)mkvno, tag | 4);
|
|
if (ret == -1) {
|
|
ret = EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
for (i = 0; i < numk; i++) {
|
|
|
|
ret = ber_printf(be, "{");
|
|
if (ret == -1) {
|
|
ret = EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
if (data[i].key_data_length[1] != 0) {
|
|
ret = ber_printf(be, "t[{t[i]",
|
|
tag | 0,
|
|
tag | 0,
|
|
(ber_int_t)data[i].key_data_type[1]);
|
|
if (ret != -1) {
|
|
ret = ber_printf(be, "t[o]",
|
|
tag | 1,
|
|
data[i].key_data_contents[1],
|
|
(ber_len_t)data[i].key_data_length[1]);
|
|
}
|
|
if (ret != -1) {
|
|
ret = ber_printf(be, "}]");
|
|
}
|
|
if (ret == -1) {
|
|
ret = EFAULT;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
ret = ber_printf(be, "t[{t[i]t[o]}]",
|
|
tag | 1,
|
|
tag | 0,
|
|
(ber_int_t)data[i].key_data_type[0],
|
|
tag | 1,
|
|
data[i].key_data_contents[0],
|
|
(ber_len_t)data[i].key_data_length[0]);
|
|
if (ret == -1) {
|
|
ret = EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
ret = ber_printf(be, "}");
|
|
if (ret == -1) {
|
|
ret = EFAULT;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
ret = ber_printf(be, "}]}");
|
|
if (ret == -1) {
|
|
ret = EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
ret = ber_flatten(be, encoded);
|
|
if (ret == -1) {
|
|
ret = EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
ber_free(be, 1);
|
|
return ret;
|
|
}
|
|
|
|
int ber_decode_krb5_key_data(struct berval *encoded, int *m_kvno,
|
|
int *numk, krb5_key_data **data)
|
|
{
|
|
krb5_key_data *keys = NULL;
|
|
BerElement *be = NULL;
|
|
void *tmp;
|
|
int i = 0;
|
|
ber_tag_t tag;
|
|
ber_int_t major_vno;
|
|
ber_int_t minor_vno;
|
|
ber_int_t kvno;
|
|
ber_int_t mkvno;
|
|
ber_int_t type;
|
|
ber_tag_t seqtag;
|
|
ber_len_t seqlen;
|
|
ber_len_t setlen;
|
|
ber_tag_t retag;
|
|
ber_tag_t opttag;
|
|
struct berval tval;
|
|
int ret;
|
|
|
|
be = ber_alloc_t(LBER_USE_DER);
|
|
if (!be) {
|
|
return ENOMEM;
|
|
}
|
|
|
|
/* reinit the ber element with the new val */
|
|
ber_init2(be, encoded, LBER_USE_DER);
|
|
|
|
/* fill key_data struct with the data */
|
|
retag = ber_scanf(be, "{t[i]t[i]t[i]t[i]t[{",
|
|
&tag, &major_vno,
|
|
&tag, &minor_vno,
|
|
&tag, &kvno,
|
|
&tag, &mkvno,
|
|
&seqtag);
|
|
if (retag == LBER_ERROR ||
|
|
major_vno != 1 ||
|
|
minor_vno != 1 ||
|
|
seqtag != (LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 4)) {
|
|
ret = EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
retag = ber_skip_tag(be, &seqlen);
|
|
|
|
/* sequence of keys */
|
|
for (i = 0; retag == LBER_SEQUENCE; i++) {
|
|
|
|
tmp = realloc(keys, (i + 1) * sizeof(krb5_key_data));
|
|
if (!tmp) {
|
|
ret = ENOMEM;
|
|
goto done;
|
|
}
|
|
keys = tmp;
|
|
|
|
memset(&keys[i], 0, sizeof(krb5_key_data));
|
|
|
|
keys[i].key_data_kvno = kvno;
|
|
|
|
/* do we have a salt type ? (optional) */
|
|
retag = ber_scanf(be, "t", &opttag);
|
|
if (retag == LBER_ERROR) {
|
|
ret = EINVAL;
|
|
goto done;
|
|
}
|
|
if (opttag == (LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0)) {
|
|
keys[i].key_data_ver = 2;
|
|
|
|
retag = ber_scanf(be, "[l{tl[i]",
|
|
&seqlen, &tag, &setlen, &type);
|
|
if (tag != (LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0)) {
|
|
ret = EINVAL;
|
|
goto done;
|
|
}
|
|
keys[i].key_data_type[1] = type;
|
|
|
|
/* do we have salt data ? (optional) */
|
|
if (seqlen > setlen + 2) {
|
|
retag = ber_scanf(be, "t[o]", &tag, &tval);
|
|
if (retag == LBER_ERROR ||
|
|
tag != (LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1)) {
|
|
ret = EINVAL;
|
|
goto done;
|
|
}
|
|
keys[i].key_data_length[1] = tval.bv_len;
|
|
keys[i].key_data_contents[1] = (krb5_octet *)tval.bv_val;
|
|
}
|
|
|
|
retag = ber_scanf(be, "}]t", &opttag);
|
|
if (retag == LBER_ERROR) {
|
|
ret = EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
} else {
|
|
keys[i].key_data_ver = 1;
|
|
}
|
|
|
|
if (opttag != (LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1)) {
|
|
ret = EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
/* get the key */
|
|
retag = ber_scanf(be, "[{t[i]t[o]}]", &tag, &type, &tag, &tval);
|
|
if (retag == LBER_ERROR) {
|
|
ret = EINVAL;
|
|
goto done;
|
|
}
|
|
keys[i].key_data_type[0] = type;
|
|
keys[i].key_data_length[0] = tval.bv_len;
|
|
keys[i].key_data_contents[0] = (krb5_octet *)tval.bv_val;
|
|
|
|
/* check for sk2params */
|
|
retag = ber_peek_tag(be, &setlen);
|
|
if (retag == (LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 2)) {
|
|
/* not supported yet, skip */
|
|
retag = ber_scanf(be, "t[x]}");
|
|
} else {
|
|
retag = ber_scanf(be, "}");
|
|
}
|
|
if (retag == LBER_ERROR) {
|
|
ret = EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
retag = ber_skip_tag(be, &seqlen);
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
done:
|
|
ber_free(be, 0); /* internal buffer is 'encoded' */
|
|
if (ret) {
|
|
for (i -= 1; keys && i >= 0; i--) {
|
|
free(keys[i].key_data_contents[0]);
|
|
free(keys[i].key_data_contents[1]);
|
|
}
|
|
free(keys);
|
|
keys = NULL;
|
|
mkvno = 0;
|
|
}
|
|
*m_kvno = mkvno;
|
|
*numk = i;
|
|
*data = keys;
|
|
return ret;
|
|
}
|
|
|
|
|
|
krb5_error_code parse_bval_key_salt_tuples(krb5_context kcontext,
|
|
const char * const *vals,
|
|
int n_vals,
|
|
krb5_key_salt_tuple **kst,
|
|
int *n_kst)
|
|
{
|
|
krb5_error_code kerr;
|
|
krb5_key_salt_tuple *ks;
|
|
int n_ks;
|
|
int i;
|
|
|
|
ks = calloc(n_vals + 1, sizeof(krb5_key_salt_tuple));
|
|
if (!ks) {
|
|
return ENOMEM;
|
|
}
|
|
|
|
for (i = 0, n_ks = 0; i < n_vals; i++) {
|
|
char *enc, *salt;
|
|
krb5_int32 tmpsalt;
|
|
krb5_enctype tmpenc;
|
|
krb5_boolean similar;
|
|
krb5_error_code krberr;
|
|
int j;
|
|
|
|
enc = strdup(vals[i]);
|
|
if (!enc) {
|
|
kerr = ENOMEM;
|
|
goto fail;
|
|
}
|
|
|
|
salt = strchr(enc, ':');
|
|
if (!salt) {
|
|
free(enc);
|
|
continue;
|
|
}
|
|
*salt = '\0'; /* null terminate the enc type */
|
|
salt++; /* skip : */
|
|
|
|
krberr = krb5_string_to_enctype(enc, &tmpenc);
|
|
if (krberr) {
|
|
free(enc);
|
|
continue;
|
|
}
|
|
|
|
krberr = krb5_string_to_salttype(salt, &tmpsalt);
|
|
for (j = 0; j < n_ks; j++) {
|
|
krb5_c_enctype_compare(kcontext,
|
|
ks[j].ks_enctype, tmpenc, &similar);
|
|
if (similar && (ks[j].ks_salttype == tmpsalt)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (j == n_ks) {
|
|
/* not found */
|
|
ks[j].ks_enctype = tmpenc;
|
|
ks[j].ks_salttype = tmpsalt;
|
|
n_ks++;
|
|
}
|
|
|
|
free(enc);
|
|
}
|
|
|
|
*kst = ks;
|
|
*n_kst = n_ks;
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
free(ks);
|
|
return kerr;
|
|
}
|
|
|
|
krb5_error_code filter_key_salt_tuples(krb5_context context,
|
|
krb5_key_salt_tuple *req, int n_req,
|
|
krb5_key_salt_tuple *supp, int n_supp,
|
|
krb5_key_salt_tuple **res, int *n_res)
|
|
{
|
|
krb5_key_salt_tuple *ks = NULL;
|
|
int n_ks;
|
|
int i, j;
|
|
|
|
ks = calloc(n_req, sizeof(krb5_key_salt_tuple));
|
|
if (!ks) {
|
|
return ENOMEM;
|
|
}
|
|
n_ks = 0;
|
|
|
|
for (i = 0; i < n_req; i++) {
|
|
for (j = 0; j < n_supp; j++) {
|
|
if (req[i].ks_enctype == supp[j].ks_enctype &&
|
|
req[i].ks_salttype == supp[j].ks_salttype) {
|
|
break;
|
|
}
|
|
}
|
|
if (j < n_supp) {
|
|
ks[n_ks] = req[i];
|
|
n_ks++;
|
|
}
|
|
}
|
|
|
|
*res = ks;
|
|
*n_res = n_ks;
|
|
return 0;
|
|
}
|
|
|
|
struct berval *create_key_control(struct keys_container *keys,
|
|
const char *principalName)
|
|
{
|
|
struct krb_key_salt *ksdata;
|
|
struct berval *bval;
|
|
BerElement *be;
|
|
int ret, i;
|
|
|
|
be = ber_alloc_t(LBER_USE_DER);
|
|
if (!be) {
|
|
return NULL;
|
|
}
|
|
|
|
ret = ber_printf(be, "{s{", principalName);
|
|
if (ret == -1) {
|
|
ber_free(be, 1);
|
|
return NULL;
|
|
}
|
|
|
|
ksdata = keys->ksdata;
|
|
for (i = 0; i < keys->nkeys; i++) {
|
|
|
|
/* we set only the EncryptionKey and salt, no s2kparams */
|
|
|
|
ret = ber_printf(be, "{t[{t[i]t[o]}]",
|
|
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0),
|
|
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0),
|
|
(ber_int_t)ksdata[i].enctype,
|
|
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1),
|
|
(char *)ksdata[i].key.contents, (ber_len_t)ksdata[i].key.length);
|
|
|
|
if (ret == -1) {
|
|
ber_free(be, 1);
|
|
return NULL;
|
|
}
|
|
|
|
if (ksdata[i].salttype == NO_SALT) {
|
|
ret = ber_printf(be, "}");
|
|
continue;
|
|
}
|
|
|
|
/* we have to pass a salt structure */
|
|
ret = ber_printf(be, "t[{t[i]t[o]}]}",
|
|
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1),
|
|
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0),
|
|
(ber_int_t)ksdata[i].salttype,
|
|
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1),
|
|
(char *)ksdata[i].salt.data, (ber_len_t)ksdata[i].salt.length);
|
|
|
|
if (ret == -1) {
|
|
ber_free(be, 1);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
ret = ber_printf(be, "}}");
|
|
if (ret == -1) {
|
|
ber_free(be, 1);
|
|
return NULL;
|
|
}
|
|
|
|
ret = ber_flatten(be, &bval);
|
|
if (ret == -1) {
|
|
ber_free(be, 1);
|
|
return NULL;
|
|
}
|
|
|
|
ber_free(be, 1);
|
|
return bval;
|
|
}
|
|
|
|
void free_keys_contents(krb5_context krbctx, struct keys_container *keys)
|
|
{
|
|
struct krb_key_salt *ksdata;
|
|
int i;
|
|
|
|
ksdata = keys->ksdata;
|
|
for (i = 0; i < keys->nkeys; i++) {
|
|
krb5_free_keyblock_contents(krbctx, &ksdata[i].key);
|
|
krb5_free_data_contents(krbctx, &ksdata[i].salt);
|
|
}
|
|
free(ksdata);
|
|
|
|
keys->ksdata = NULL;
|
|
keys->nkeys = 0;
|
|
}
|
|
|
|
/* Determines Encryption and Salt types,
|
|
* allocates key_salt data storage,
|
|
* filters out equivalent encodings,
|
|
* returns 0 if no enctypes available, >0 if enctypes are available */
|
|
static int prep_ksdata(krb5_context krbctx, const char *str,
|
|
struct keys_container *keys,
|
|
char **err_msg)
|
|
{
|
|
struct krb_key_salt *ksdata;
|
|
krb5_error_code krberr;
|
|
int n, i, j, nkeys;
|
|
|
|
*err_msg = NULL;
|
|
|
|
if (str == NULL) {
|
|
krb5_enctype *ktypes;
|
|
|
|
krberr = krb5_get_permitted_enctypes(krbctx, &ktypes);
|
|
if (krberr) {
|
|
*err_msg = _("No system preferred enctypes ?!\n");
|
|
return 0;
|
|
}
|
|
|
|
for (n = 0; ktypes[n]; n++) /* count */ ;
|
|
|
|
ksdata = calloc(n + 1, sizeof(struct krb_key_salt));
|
|
if (NULL == ksdata) {
|
|
*err_msg = _("Out of memory!?\n");
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < n; i++) {
|
|
ksdata[i].enctype = ktypes[i];
|
|
ksdata[i].salttype = KRB5_KDB_SALTTYPE_NORMAL;
|
|
}
|
|
|
|
ipa_krb5_free_ktypes(krbctx, ktypes);
|
|
|
|
nkeys = i;
|
|
|
|
} else {
|
|
char *tmp, *t, *p, *q;
|
|
|
|
t = tmp = strdup(str);
|
|
if (!tmp) {
|
|
*err_msg = _("Out of memory\n");
|
|
return 0;
|
|
}
|
|
|
|
/* count */
|
|
n = 0;
|
|
while ((p = strchr(t, ','))) {
|
|
t = p+1;
|
|
n++;
|
|
}
|
|
n++; /* count the last one that is 0 terminated instead */
|
|
|
|
/* at the end we will have at most n entries + 1 terminating */
|
|
ksdata = calloc(n + 1, sizeof(struct krb_key_salt));
|
|
if (!ksdata) {
|
|
*err_msg = _("Out of memory\n");
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0, j = 0, t = tmp; i < n; i++) {
|
|
|
|
p = strchr(t, ',');
|
|
if (p) *p = '\0';
|
|
|
|
q = strchr(t, ':');
|
|
if (q) *q++ = '\0';
|
|
|
|
krberr = krb5_string_to_enctype(t, &ksdata[j].enctype);
|
|
if (krberr != 0) {
|
|
*err_msg = _("Warning unrecognized encryption type.\n");
|
|
if (p) t = p + 1;
|
|
continue;
|
|
}
|
|
if (p) t = p + 1;
|
|
|
|
if (!q) {
|
|
ksdata[j].salttype = KRB5_KDB_SALTTYPE_NORMAL;
|
|
j++;
|
|
continue;
|
|
}
|
|
|
|
krberr = krb5_string_to_salttype(q, &ksdata[j].salttype);
|
|
if (krberr != 0) {
|
|
*err_msg = _("Warning unrecognized salt type.\n");
|
|
continue;
|
|
}
|
|
|
|
j++;
|
|
}
|
|
|
|
nkeys = j;
|
|
|
|
free(tmp);
|
|
}
|
|
|
|
/* Check we don't already have a key with a similar encoding,
|
|
* it would just produce redundant data and this is what the
|
|
* MIT code do anyway */
|
|
|
|
for (i = 0, n = 0; i < nkeys; i++ ) {
|
|
krb5_boolean similar = 0;
|
|
|
|
for (j = 0; j < i; j++) {
|
|
krberr = krb5_c_enctype_compare(krbctx,
|
|
ksdata[j].enctype,
|
|
ksdata[i].enctype,
|
|
&similar);
|
|
if (krberr) {
|
|
free_keys_contents(krbctx, keys);
|
|
free(ksdata);
|
|
*err_msg = _("Enctype comparison failed!\n");
|
|
return 0;
|
|
}
|
|
if (similar &&
|
|
(ksdata[j].salttype == ksdata[i].salttype)) {
|
|
break;
|
|
}
|
|
}
|
|
if (j < i) {
|
|
/* redundant encoding, remove it, and shift others */
|
|
int x;
|
|
for (x = i; x < nkeys-1; x++) {
|
|
ksdata[x].enctype = ksdata[x+1].enctype;
|
|
ksdata[x].salttype = ksdata[x+1].salttype;
|
|
}
|
|
continue;
|
|
}
|
|
/* count only confirmed enc/salt tuples */
|
|
n++;
|
|
}
|
|
|
|
keys->nkeys = n;
|
|
keys->ksdata = ksdata;
|
|
|
|
return n;
|
|
}
|
|
|
|
int create_keys(krb5_context krbctx,
|
|
krb5_principal princ,
|
|
char *password,
|
|
const char *enctypes_string,
|
|
struct keys_container *keys,
|
|
char **err_msg)
|
|
{
|
|
struct krb_key_salt *ksdata;
|
|
krb5_error_code krberr;
|
|
krb5_data key_password;
|
|
krb5_data *realm = NULL;
|
|
int i, nkeys;
|
|
int ret;
|
|
|
|
*err_msg = NULL;
|
|
|
|
ret = prep_ksdata(krbctx, enctypes_string, keys, err_msg);
|
|
if (ret == 0) return 0;
|
|
|
|
ksdata = keys->ksdata;
|
|
nkeys = keys->nkeys;
|
|
|
|
if (password) {
|
|
key_password.data = password;
|
|
key_password.length = strlen(password);
|
|
|
|
realm = krb5_princ_realm(krbctx, princ);
|
|
}
|
|
|
|
for (i = 0; i < nkeys; i++) {
|
|
krb5_data *salt;
|
|
|
|
if (!password) {
|
|
/* cool, random keys */
|
|
krberr = krb5_c_make_random_key(krbctx,
|
|
ksdata[i].enctype,
|
|
&ksdata[i].key);
|
|
if (krberr) {
|
|
*err_msg = _("Failed to create random key!\n");
|
|
return 0;
|
|
}
|
|
/* set the salt to NO_SALT as the key was random */
|
|
ksdata[i].salttype = NO_SALT;
|
|
continue;
|
|
}
|
|
|
|
/* Make keys using password and required salt */
|
|
switch (ksdata[i].salttype) {
|
|
case KRB5_KDB_SALTTYPE_ONLYREALM:
|
|
krberr = krb5_copy_data(krbctx, realm, &salt);
|
|
if (krberr) {
|
|
*err_msg = _("Failed to create key!\n");
|
|
return 0;
|
|
}
|
|
|
|
ksdata[i].salt.length = salt->length;
|
|
ksdata[i].salt.data = malloc(salt->length);
|
|
if (!ksdata[i].salt.data) {
|
|
*err_msg = _("Out of memory!\n");
|
|
return 0;
|
|
}
|
|
memcpy(ksdata[i].salt.data, salt->data, salt->length);
|
|
krb5_free_data(krbctx, salt);
|
|
break;
|
|
|
|
case KRB5_KDB_SALTTYPE_NOREALM:
|
|
krberr = ipa_krb5_principal2salt_norealm(krbctx, princ,
|
|
&ksdata[i].salt);
|
|
if (krberr) {
|
|
*err_msg = _("Failed to create key!\n");
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case KRB5_KDB_SALTTYPE_NORMAL:
|
|
krberr = krb5_principal2salt(krbctx, princ, &ksdata[i].salt);
|
|
if (krberr) {
|
|
*err_msg = _("Failed to create key!\n");
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
/* no KRB5_KDB_SALTTYPE_V4, we do not support krb v4 */
|
|
|
|
case KRB5_KDB_SALTTYPE_AFS3:
|
|
/* Comment from MIT sources:
|
|
* * Why do we do this? Well, the afs_mit_string_to_key
|
|
* * needs to use strlen, and the realm is not NULL
|
|
* * terminated....
|
|
*/
|
|
ksdata[i].salt.data = (char *)malloc(realm->length + 1);
|
|
if (NULL == ksdata[i].salt.data) {
|
|
*err_msg = _("Out of memory!\n");
|
|
return 0;
|
|
}
|
|
memcpy((char *)ksdata[i].salt.data,
|
|
(char *)realm->data, realm->length);
|
|
ksdata[i].salt.data[realm->length] = '\0';
|
|
/* AFS uses a special length (UGLY) */
|
|
ksdata[i].salt.length = SALT_TYPE_AFS_LENGTH;
|
|
break;
|
|
|
|
default:
|
|
*err_msg = _("Bad or unsupported salt type.\n");
|
|
/* FIXME:
|
|
fprintf(stderr, _("Bad or unsupported salt type (%d)!\n"),
|
|
ksdata[i].salttype);
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
krberr = krb5_c_string_to_key(krbctx,
|
|
ksdata[i].enctype,
|
|
&key_password,
|
|
&ksdata[i].salt,
|
|
&ksdata[i].key);
|
|
if (krberr) {
|
|
*err_msg = _("Failed to create key!\n");
|
|
return 0;
|
|
}
|
|
|
|
/* set back salt length to real value if AFS3 */
|
|
if (ksdata[i].salttype == KRB5_KDB_SALTTYPE_AFS3) {
|
|
ksdata[i].salt.length = realm->length;
|
|
}
|
|
}
|
|
|
|
return nkeys;
|
|
}
|
|
|