summaryrefslogtreecommitdiffstats
path: root/ipa-client/ipa-getkeytab.c
diff options
context:
space:
mode:
Diffstat (limited to 'ipa-client/ipa-getkeytab.c')
-rw-r--r--ipa-client/ipa-getkeytab.c310
1 files changed, 305 insertions, 5 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;