summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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,