diff options
author | Simo Sorce <simo@redhat.com> | 2013-09-19 12:50:35 -0400 |
---|---|---|
committer | Martin Kosek <mkosek@redhat.com> | 2014-06-26 10:30:53 +0200 |
commit | f352702d6785fc5f59698dba73d415f994b4ce7d (patch) | |
tree | a6e51d2f5f2cabbfe1d77414f3239413e56ace96 /ipa-client | |
parent | 153a009a07e94935261d188b9d1a0f2638b22a80 (diff) | |
download | freeipa-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>
Diffstat (limited to 'ipa-client')
-rw-r--r-- | ipa-client/ipa-getkeytab.c | 310 |
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; |