summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2013-09-19 12:50:35 -0400
committerMartin Kosek <mkosek@redhat.com>2014-06-26 10:30:53 +0200
commitf352702d6785fc5f59698dba73d415f994b4ce7d (patch)
treea6e51d2f5f2cabbfe1d77414f3239413e56ace96
parent153a009a07e94935261d188b9d1a0f2638b22a80 (diff)
downloadfreeipa-f352702d6785fc5f59698dba73d415f994b4ce7d.tar.gz
freeipa-f352702d6785fc5f59698dba73d415f994b4ce7d.tar.xz
freeipa-f352702d6785fc5f59698dba73d415f994b4ce7d.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 Reviewed-By: Nathaniel McCallum <npmccallum@redhat.com>
-rw-r--r--ipa-client/ipa-getkeytab.c310
-rw-r--r--util/ipa_krb5.c130
-rw-r--r--util/ipa_krb5.h3
3 files changed, 383 insertions, 60 deletions
diff --git a/ipa-client/ipa-getkeytab.c b/ipa-client/ipa-getkeytab.c
index 506fff1bb..74a8800bc 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,282 @@ 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 {
+ * newkeys [0] Newkeys,
+ * curkeys [1] CurrentKeys,
+ * reply [2] Reply
+ * }
+ *
+ * NewKeys ::= SEQUENCE {
+ * serviceIdentity [0] OCTET STRING,
+ * enctypes [1] SEQUENCE OF Int16
+ * password [2] OCTET STRING OPTIONAL,
+ * }
+ *
+ * CurrentKeys ::= SEQUENCE {
+ * serviceIdentity [0] OCTET STRING,
+ * }
+ *
+ * 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
+ * }
+ */
+
+#define GK_REQUEST_NEWKEYS (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 0)
+#define GK_REQUEST_CURKEYS (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 1)
+#define GKREQ_SVCNAME_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 1)
+#define GKREQ_ENCTYPES_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 1)
+#define GKREQ_PASSWORD_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 2)
+
+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;
+ }
+
+ if (gen) {
+ ctag = GK_REQUEST_NEWKEYS;
+ } else {
+ ctag = GK_REQUEST_CURKEYS;
+ }
+
+ ret = ber_printf(be, "t{t[s]", ctag, GKREQ_SVCNAME_TAG, svc_princ);
+ if (ret == -1) {
+ ber_free(be, 1);
+ goto done;
+ }
+
+ if (gen) {
+ ret = ber_printf(be, "t{", GKREQ_ENCTYPES_TAG);
+ 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;
+ }
+
+ if (password) {
+ ret = ber_printf(be, "t[s]", GKREQ_PASSWORD_TAG, password);
+ 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;
+}
+
+#define GK_REPLY_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 2)
+#define GKREP_KEY_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 0)
+#define GKREP_SALT_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 1)
+
+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;
+
+ *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 || ctag != GK_REPLY_TAG) {
+ *err_msg = _("Failed to parse control head!\n");
+ ret = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ keys->nkeys = 0;
+ keys->ksdata = NULL;
+
+ rtag = ber_peek_tag(ber, &tlen);
+ for (int i = 0; rtag == LBER_SEQUENCE; i++) {
+ 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 || ctag != GKREP_KEY_TAG) {
+ *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 == GKREP_SALT_TAG) {
+ 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);
+ }
+
+ 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 +759,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 +784,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 +797,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 +855,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 +907,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 +931,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..6334ed3b0 100644
--- a/util/ipa_krb5.c
+++ b/util/ipa_krb5.c
@@ -779,6 +779,79 @@ 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;
+ int count;
+ int num;
+
+ *err_msg = NULL;
+
+ tmp = strdup(str);
+ if (!tmp) {
+ *err_msg = _("Out of memory\n");
+ return ENOMEM;
+ }
+
+ /* count */
+ count = 0;
+ for (t = tmp; t; t = strchr(t, ',')) {
+ count++;
+ t++;
+ }
+ count++; /* count the last one that is 0 terminated instead */
+
+ /* at the end we will have at most count entries + 1 terminating */
+ ksdata = calloc(count + 1, sizeof(struct krb_key_salt));
+ if (!ksdata) {
+ *err_msg = _("Out of memory\n");
+ free(tmp);
+ return ENOMEM;
+ }
+
+ num = 0;
+ t = tmp;
+ for (int i = 0; i < count; i++) {
+ char *p, *q;
+
+ p = strchr(t, ',');
+ if (p) *p = '\0';
+
+ q = strchr(t, ':');
+ if (q) *q++ = '\0';
+
+ krberr = krb5_string_to_enctype(t, &ksdata[num].enctype);
+ if (krberr) {
+ *err_msg = _("Warning unrecognized encryption type.\n");
+ if (p) t = p + 1;
+ continue;
+ }
+ if (p) t = p + 1;
+
+ if (!q) {
+ ksdata[num].salttype = KRB5_KDB_SALTTYPE_NORMAL;
+ num++;
+ continue;
+ }
+
+ krberr = krb5_string_to_salttype(q, &ksdata[num].salttype);
+ if (krberr) {
+ *err_msg = _("Warning unrecognized salt type.\n");
+ continue;
+ }
+
+ num++;
+ }
+
+ *num_encsalts = num;
+ *encsalts = ksdata;
+ free(tmp);
+ return 0;
+}
+
/* Determines Encryption and Salt types,
* allocates key_salt data storage,
* filters out equivalent encodings,
@@ -820,63 +893,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,