summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2013-09-19 12:50:35 -0400
committerSimo Sorce <simo@redhat.com>2014-06-09 14:53:55 -0400
commit63f7aa46f2e1d0f4dec7951cc1684f555fb77d39 (patch)
treef65635f54069496900bfe575ffd083bcaf488237
parenta8310c2486a192a573f67947df79199bb3eaecd5 (diff)
downloadfreeipa-63f7aa46f2e1d0f4dec7951cc1684f555fb77d39.tar.gz
freeipa-63f7aa46f2e1d0f4dec7951cc1684f555fb77d39.tar.xz
freeipa-63f7aa46f2e1d0f4dec7951cc1684f555fb77d39.zip
ipa-getkeytab: Add support for get_keytab extop
This new extended operation is tried by default and then the code falls back to the old method if it fails. The new method allows for server side password generation as well as retrieval of existing credentials w/o causing regeneration of keys on the server. Resolves: https://fedorahosted.org/freeipa/ticket/3859
-rw-r--r--ipa-client/ipa-getkeytab.c299
-rw-r--r--util/ipa_krb5.c126
-rw-r--r--util/ipa_krb5.h3
3 files changed, 368 insertions, 60 deletions
diff --git a/ipa-client/ipa-getkeytab.c b/ipa-client/ipa-getkeytab.c
index dbd4ed4d2..7307f094c 100644
--- a/ipa-client/ipa-getkeytab.c
+++ b/ipa-client/ipa-getkeytab.c
@@ -28,6 +28,7 @@
#include <stdarg.h>
#include <stdlib.h>
#include <stdint.h>
+#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <time.h>
@@ -431,7 +432,271 @@ error_out:
if (ld) ldap_unbind_ext(ld, NULL, NULL);
if (control) ber_bvfree(control);
free(encs);
- return 0;
+ return -1;
+}
+
+/* Format of getkeytab control
+ *
+ * KeytabGetRequest ::= CHOICE {
+ * request [0] Request,
+ * reply [1] Reply
+ * }
+ *
+ * Request :: SEQUENCE {
+ * serviceIdentity [0] OCTET STRING,
+ * getNew [1] Boolean,
+ * password [2] OCTET STRING OPTIONAL,
+ * enctypes [3] SEQUENCE OF Int16 OPTIONAL
+ * }
+ *
+ * Reply :: SEQUENCE {
+ * new_kvno Int32
+ * keys SEQUENCE OF KrbKey,
+ * }
+ *
+ * KrbKey ::= SEQUENCE {
+ * key [0] EncryptionKey,
+ * salt [1] KrbSalt OPTIONAL,
+ * s2kparams [2] OCTET STRING OPTIONAL,
+ * }
+ *
+ * EncryptionKey ::= SEQUENCE {
+ * keytype [0] Int32,
+ * keyvalue [1] OCTET STRING
+ * }
+ *
+ * KrbSalt ::= SEQUENCE {
+ * type [0] Int32,
+ * salt [1] OCTET STRING
+ * }
+ */
+
+static struct berval *create_getkeytab_control(const char *svc_princ, bool gen,
+ const char *password,
+ struct krb_key_salt *encsalts,
+ int num_encsalts)
+{
+ struct berval *bval = NULL;
+ BerElement *be;
+ ber_tag_t ctag;
+ ber_int_t e;
+ int ret, i;
+
+ be = ber_alloc_t(LBER_USE_DER);
+ if (!be) {
+ return NULL;
+ }
+
+ ctag = LBER_CONSTRUCTED | LBER_CLASS_CONTEXT;
+
+ ret = ber_printf(be, "t[{t[s]t[b]", (ctag | 0),
+ (ctag | 0), svc_princ,
+ (ctag | 1), (ber_int_t)(gen ? 1 : 0));
+ if (ret == -1) {
+ ber_free(be, 1);
+ goto done;
+ }
+
+ if (password) {
+ ret = ber_printf(be, "t[s]", (ctag | 2), password);
+ if (ret == -1) {
+ ber_free(be, 1);
+ goto done;
+ }
+ }
+
+ if (num_encsalts) {
+ ret = ber_printf(be, "t[{", (ctag | 3));
+ if (ret == -1) {
+ ber_free(be, 1);
+ goto done;
+ }
+
+ for (i = 0; i < num_encsalts; i++) {
+ e = encsalts[i].enctype;
+ ret = ber_printf(be, "i", e);
+ if (ret == -1) {
+ ber_free(be, 1);
+ goto done;
+ }
+ }
+
+ ret = ber_printf(be, "}]");
+ if (ret == -1) {
+ ber_free(be, 1);
+ goto done;
+ }
+ }
+
+ ret = ber_printf(be, "}]");
+ if (ret == -1) {
+ ber_free(be, 1);
+ goto done;
+ }
+
+ ret = ber_flatten(be, &bval);
+ if (ret == -1) {
+ ber_free(be, 1);
+ goto done;
+ }
+
+done:
+ ber_free(be, 1);
+ return bval;
+}
+
+static int ldap_get_keytab(krb5_context krbctx, bool generate, char *password,
+ const char *enctypes, const char *bind_server,
+ const char *svc_princ, krb5_principal bind_princ,
+ const char *bind_dn, const char *bind_pw,
+ struct keys_container *keys, int *kvno,
+ char **err_msg)
+{
+ struct krb_key_salt *es = NULL;
+ int num_es = 0;
+ struct berval *control = NULL;
+ LDAP *ld;
+ LDAPControl **srvctrl = NULL;
+ BerElement *ber = NULL;
+ ber_tag_t rtag;
+ ber_tag_t ctag;
+ ber_len_t tlen;
+ ber_int_t vno;
+ ber_int_t tint;
+ struct berval tbval;
+ int ret;
+ int i;
+
+ *err_msg = NULL;
+
+ if (enctypes) {
+ ret = ipa_string_to_enctypes(enctypes, &es, &num_es, err_msg);
+ if (ret || num_es == 0) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+ }
+
+ control = create_getkeytab_control(svc_princ, generate,
+ password, es, num_es);
+ if (!control) {
+ *err_msg = _("Failed to create control!\n");
+ ret = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ ret = ipa_ldap_bind(bind_server, bind_princ, bind_dn, bind_pw, &ld);
+ if (ret != LDAP_SUCCESS) {
+ *err_msg = _("Failed to bind to server!\n");
+ goto done;
+ }
+
+ /* perform extedned opt to get keytab */
+ ret = ipa_ldap_extended_op(ld, KEYTAB_GET_OID, control, &srvctrl);
+ if (ret != LDAP_SUCCESS) {
+ goto done;
+ }
+
+ ber = get_control_data(srvctrl, KEYTAB_GET_OID);
+ if (!ber) {
+ *err_msg = _("Failed to find or parse reply control!\n");
+ ret = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ rtag = ber_scanf(ber, "t[{i{", &ctag, &vno);
+ if (rtag == LBER_ERROR) {
+ *err_msg = _("Failed to parse vno in control!\n");
+ ret = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ i = 0;
+ keys->nkeys = 0;
+ keys->ksdata = NULL;
+
+ rtag = ber_peek_tag(ber, &tlen);
+ while (rtag == LBER_SEQUENCE) {
+ if ((i % 5) == 0) {
+ struct krb_key_salt *ksdata;
+ ksdata = realloc(keys->ksdata,
+ (i + 5) * sizeof(struct krb_key_salt));
+ if (!ksdata) {
+ *err_msg = _("Out of memory!\n");
+ ret = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+ keys->ksdata = ksdata;
+ }
+ memset(&keys->ksdata[i], 0, sizeof(struct krb_key_salt));
+ keys->nkeys = i + 1;
+
+ rtag = ber_scanf(ber, "{t[{[i][o]}]", &ctag, &tint, &tbval);
+ if (rtag == LBER_ERROR) {
+ *err_msg = _("Failed to parse enctype in key data!\n");
+ ret = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+ keys->ksdata[i].enctype = tint;
+ keys->ksdata[i].key.enctype = tint;
+ keys->ksdata[i].key.length = tbval.bv_len;
+ keys->ksdata[i].key.contents = malloc(tbval.bv_len);
+ if (!keys->ksdata[i].key.contents) {
+ *err_msg = _("Out of memory!\n");
+ ret = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+ memcpy(keys->ksdata[i].key.contents, tbval.bv_val, tbval.bv_len);
+ ber_memfree(tbval.bv_val);
+
+ rtag = ber_peek_tag(ber, &tlen);
+ if (rtag == (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1)) {
+ rtag = ber_scanf(ber, "t[{[i][o]}]", &ctag, &tint, &tbval);
+ if (rtag == LBER_ERROR) {
+ *err_msg = _("Failed to parse salt in key data!\n");
+ ret = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+ keys->ksdata[i].salttype = tint;
+ keys->ksdata[i].salt.length = tbval.bv_len;
+ keys->ksdata[i].salt.data = malloc(tbval.bv_len);
+ if (!keys->ksdata[i].salt.data) {
+ *err_msg = _("Out of memory!\n");
+ ret = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+ memcpy(keys->ksdata[i].salt.data, tbval.bv_val, tbval.bv_len);
+ ber_memfree(tbval.bv_val);
+ }
+ rtag = ber_scanf(ber, "}");
+ if (rtag == LBER_ERROR) {
+ *err_msg = _("Failed to parse ending of key data!\n");
+ ret = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ rtag = ber_peek_tag(ber, &tlen);
+ i++;
+ }
+
+ rtag = ber_scanf(ber, "}}]");
+ if (rtag == LBER_ERROR) {
+ *err_msg = _("Failed to parse ending of control!\n");
+ ret = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ *kvno = vno;
+ ret = LDAP_SUCCESS;
+
+done:
+ if (ber) ber_free(ber, 1);
+ if (ld) ldap_unbind_ext(ld, NULL, NULL);
+ if (control) ber_bvfree(control);
+ free(es);
+ if (ret) {
+ free_keys_contents(krbctx, keys);
+ }
+ return ret;
}
static char *ask_password(krb5_context krbctx)
@@ -483,6 +748,7 @@ int main(int argc, const char *argv[])
int quiet = 0;
int askpass = 0;
int permitted_enctypes = 0;
+ int retrieve = 0;
struct poptOption options[] = {
{ "quiet", 'q', POPT_ARG_NONE, &quiet, 0,
_("Print as little as possible"), _("Output only on errors")},
@@ -507,6 +773,8 @@ int main(int argc, const char *argv[])
_("LDAP DN"), _("DN to bind as if not using kerberos") },
{ "bindpw", 'w', POPT_ARG_STRING, &bindpw, 0,
_("LDAP password"), _("password to use if not using kerberos") },
+ { "retrieve", 'r', POPT_ARG_NONE, &retrieve, 0,
+ _("Retrieve current keys without changing them"), NULL },
POPT_AUTOHELP
POPT_TABLEEND
};
@@ -518,7 +786,7 @@ int main(int argc, const char *argv[])
krb5_principal uprinc;
krb5_principal sprinc;
krb5_error_code krberr;
- struct keys_container keys;
+ struct keys_container keys = { 0 };
krb5_keytab kt;
int kvno;
int i, ret;
@@ -576,6 +844,11 @@ int main(int argc, const char *argv[])
exit(10);
}
+ if (askpass && retrieve) {
+ fprintf(stderr, _("Incompatible options provided (-r and -P)\n"));
+ exit(2);
+ }
+
if (askpass) {
password = ask_password(krbctx);
if (!password) {
@@ -623,6 +896,19 @@ int main(int argc, const char *argv[])
exit(7);
}
+ kvno = -1;
+ ret = ldap_get_keytab(krbctx, (retrieve == 0), password, enctypes_string,
+ server, principal, uprinc, binddn, bindpw,
+ &keys, &kvno, &err_msg);
+ if (ret) {
+ if (!quiet && err_msg != NULL) {
+ fprintf(stderr, "%s", err_msg);
+ }
+ }
+
+ if (password && (retrieve == 0) && (kvno == -1)) {
+ if (!quiet) fprintf(stderr, _("Retrying with old method\n"));
+
/* create key material */
ret = create_keys(krbctx, sprinc, password, enctypes_string, &keys, &err_msg);
if (!ret) {
@@ -634,9 +920,12 @@ int main(int argc, const char *argv[])
}
kvno = ldap_set_keytab(krbctx, server, principal, uprinc, binddn, bindpw, &keys);
- if (!kvno) {
- exit(9);
- }
+ }
+
+ if (kvno == -1) {
+ fprintf(stderr, _("Failed to get keytab\n"));
+ exit(9);
+ }
for (i = 0; i < keys.nkeys; i++) {
krb5_keytab_entry kt_entry;
diff --git a/util/ipa_krb5.c b/util/ipa_krb5.c
index 2a94b1944..d44fb2cdb 100644
--- a/util/ipa_krb5.c
+++ b/util/ipa_krb5.c
@@ -779,6 +779,75 @@ void free_keys_contents(krb5_context krbctx, struct keys_container *keys)
keys->nkeys = 0;
}
+int ipa_string_to_enctypes(const char *str, struct krb_key_salt **encsalts,
+ int *num_encsalts, char **err_msg)
+{
+ struct krb_key_salt *ksdata;
+ krb5_error_code krberr;
+ char *tmp, *t, *p, *q;
+ int i, j, n;
+
+ *err_msg = NULL;
+
+ t = tmp = strdup(str);
+ if (!tmp) {
+ *err_msg = _("Out of memory\n");
+ return ENOMEM;
+ }
+
+ /* 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");
+ free(tmp);
+ return ENOMEM;
+ }
+
+ 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) {
+ *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) {
+ *err_msg = _("Warning unrecognized salt type.\n");
+ continue;
+ }
+
+ j++;
+ }
+ free(tmp);
+
+ *num_encsalts = j;
+ *encsalts = ksdata;
+ return 0;
+}
+
/* Determines Encryption and Salt types,
* allocates key_salt data storage,
* filters out equivalent encodings,
@@ -820,63 +889,10 @@ static int prep_ksdata(krb5_context krbctx, const char *str,
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");
+ krberr = ipa_string_to_enctypes(str, &ksdata, &nkeys, err_msg);
+ if (krberr) {
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,
diff --git a/util/ipa_krb5.h b/util/ipa_krb5.h
index 2431fd70b..1e036e4f8 100644
--- a/util/ipa_krb5.h
+++ b/util/ipa_krb5.h
@@ -69,6 +69,9 @@ void free_keys_contents(krb5_context krbctx, struct keys_container *keys);
struct berval *create_key_control(struct keys_container *keys,
const char *principalName);
+int ipa_string_to_enctypes(const char *str, struct krb_key_salt **encsalts,
+ int *num_encsalts, char **err_msg);
+
int create_keys(krb5_context krbctx,
krb5_principal princ,
char *password,