On Fri, 2014-06-13 at 14:29 -0400, Rob Crittenden wrote:
> Rob Crittenden wrote:
> > Simo Sorce wrote:
> >> On Wed, 2014-06-11 at 17:03 -0400, Rob Crittenden wrote:
> >>> 0001
> >>>
> >>> When is_allowed_to_access_attr() fails it should include the value of
> >>> access in the error log for debugging.
> >>
> >> Ok added more detailed logging
> >>
> >>> Nit: Coluld not fetch REALM backend
> >>
> >> Fixed
> >>
> >>> There are still a ton of "ber_scanf failed" duplicated fatal errors. I'm
> >>> fine keeping a common err_msg but the fatal error should be unique.
> >>
> >> Yeah thanks to this comment, I had a small change of heart.
> >> Instead of sending such detailed information to clients I reverted to
> >> send a little less information to the clients and instead LOG_FATAL in a
> >> more detailed way. HTH
> >>
> >>> This breaks normal host delegation. If you add a host to another host's
> >>> managedby, getting the keytab will fail. This is due to:
> >>>
> >>> [11/Jun/2014:16:56:45 -0400] NSACLPlugin - conn=4 op=3 (main): Deny
> >>> write on
> >>> entry(fqdn=client2.example.com,cn=computers,cn=accounts,dc=example,dc=com).attr(ipaProtectedOperation;write_keys)
> >>> to fqdn=client1.example.com,cn=computers,cn=accounts,dc=example,dc=com:
> >>> no aci matched the subject by aci(97): aciname= "Groups allowed to
> >>> create keytab keys", acidn="cn=accounts,dc=example,dc=com"
> >>
> >> Ok this should be working now, I added a new ACI to allow also
> >> managedby#USERDN to operate on keytabs.
> >>
> >> New patches attached.
> > 
> > Functionally these seem to work ok. I think there should be some
> > documented way to enable the -r in ipa-getkeytab. Right now I'm not even
> > entirely sure how one would add a permission to do so.
> 
> NACK
> 
> Simo noticed that the ACIs are in cn=accounts. On the one hand this is a
> reasonable place to put these, on the other it is a bit broad.
> 
> I think we'll need type-specific ACIs in a number of subtrees: users,
> computers and services.

[Only patch 3 attached, as none of the others changed, and addiotional
discussion is needed, see below.]

Ok after looking carefully into this problem I see that there are really
2 issues.
1) managedby for users, we do not want someone adding a managedby
attribute to inadvertently allow the manager to set a user's password.

So I changed that ACI and restricted it only to ipaHost and ipaService
objects (tested).

I haven't touched any other ACI because in order to use them you need to
have intentionally added an ipaAllowedToPerform attribute to the user
entry.

2) and I think this is a MUCH bigger issue, the Admin users are
unbounded and pass any Access Control Check and this means they can now
retrieve any key for users or machines.
It is already bad enough that admins can unconditionally set any key,
but this at least leaves back a pretty big trail (the original client
password/key fails to work), and is a necessary evil (password resets,
hosts creation/recovery).
But I am not very comfortable with the idea an admin can retrieve any
key without actually ending up changing it. Petr do we have any short
term plan to address the Admin's super ACI ?

Otherwise we can add ipaProtectedOperation in the exclude list for the
superACI ("Admins can manage any entry") and add the following ACI in
cn=accounts, to restore admin ability to set keys (but not retrieve
them):

aci: (targetattr="ipaProtectedOperation;write_keys")(version 3.0; acl
 "Admins are allowed to rekey any entity"; allow(write) groupdn="ldap
 :///cn=admins,cn=groups,cn=accounts,$SUFFIX";)

I tested this combination and it effectively stops admin from retrieving
all keys unless explicitly authorize by positive
ACIs/ipaAllowedToPerform attributes.


Thoughts ?

-- 
Simo Sorce * Red Hat, Inc * New York
>From e3a19bf910f2e2bbaabb870471045597469e790e Mon Sep 17 00:00:00 2001
From: Simo Sorce <s...@redhat.com>
Date: Tue, 17 Sep 2013 00:30:14 -0400
Subject: [PATCH 3/6] keytab: Add new extended operation to get a keytab.

This new extended operation allow to create new keys or retrieve
existing ones. The new set of keys is returned as a ASN.1 structure
similar to the one that is passed in by the 'set keytab' extended
operation.

Access to the operation is regulated through a new special ACI that
allows 'retrieval' only if the user has access to an attribute named
ipaProtectedOperation postfixed by the subtypes 'read_keys' and
'write_keys' to distinguish between creation and retrieval operation.

For example for allowing retrieval by a specific user the following ACI
is set on cn=accounts:

(targetattr="ipaProtectedOperation;read_keys") ...
 ... userattr=ipaAllowedToPerform;read_keys#USERDN)

This ACI matches only if the service object hosts a new attribute named
ipaAllowedToPerform that holds the DN of the user attempting the
operation.

Resolves:
https://fedorahosted.org/freeipa/ticket/3859
---
 .../ipa-pwd-extop/ipa_pwd_extop.c                  | 571 +++++++++++++++++++++
 install/share/60basev3.ldif                        |   3 +
 install/share/default-aci.ldif                     |   8 +-
 install/updates/20-aci.update                      |   9 +
 util/ipa_krb5.h                                    |   1 +
 5 files changed, 591 insertions(+), 1 deletion(-)

diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
index c0bb9fda26172699e9ae7628f61b763c746188fe..e0f90832400dd470b18e21177c135f2855b6f239 100644
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
@@ -1268,6 +1268,571 @@ free_and_return:
 	return SLAPI_PLUGIN_EXTENDED_SENT_RESULT;
 }
 
+/* Format of getkeytab request
+ *
+ * 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
+ * }
+ */
+
+#define GKREQ_PASSWORD_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 2)
+#define GKREQ_ENCTYPES_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 3)
+
+static int decode_getkeytab_request(struct berval *extop, bool *wantold,
+                                    char **_svcname, char **_password,
+                                    krb5_key_salt_tuple **kenctypes,
+                                    int *num_kenctypes, char **_err_msg)
+{
+    int rc = LDAP_OPERATIONS_ERROR;
+    char *err_msg = NULL;
+    BerElement *ber = NULL;
+    ber_len_t tlen;
+    ber_tag_t rtag;
+    ber_tag_t ctag;
+    ber_int_t getnew = 0;
+    char *svcname = NULL;
+    char *password = NULL;
+    ber_int_t enctype;
+    krb5_key_salt_tuple *enctypes = NULL;
+    int num = 0;
+
+    ber = ber_init(extop);
+    if (ber == NULL) {
+        err_msg = "KeytabGet Request decode failed.\n";
+        rc = LDAP_PROTOCOL_ERROR;
+        goto done;
+    }
+
+    /* check this is a request */
+    rtag = ber_peek_tag(ber, &tlen);
+    if (rtag != (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 0)) {
+        LOG_FATAL("ber_peek_tag failed\n");
+        err_msg = "Invalid payload.\n";
+        rc = LDAP_PROTOCOL_ERROR;
+        goto done;
+    }
+
+    /* ber parse code */
+    rtag = ber_scanf(ber, "t[{t[a]", &ctag, &ctag, &svcname);
+    if (rtag == LBER_ERROR) {
+        LOG_FATAL("ber_scanf failed to decode service name\n");
+        err_msg = "Invalid payload.\n";
+        rc = LDAP_PROTOCOL_ERROR;
+        goto done;
+    }
+
+    rtag = ber_scanf(ber, "t[b]", &ctag, &getnew);
+    if (rtag == LBER_ERROR) {
+        LOG_FATAL("ber_scanf failed to decode request type\n");
+        err_msg = "Invalid payload.\n";
+        rc = LDAP_PROTOCOL_ERROR;
+        goto done;
+    }
+
+    rtag = ber_peek_tag(ber, &tlen);
+    if (rtag == GKREQ_PASSWORD_TAG) {
+        /* optional password present */
+        rtag = ber_scanf(ber, "[a]", &password);
+        if (rtag == LBER_ERROR) {
+            LOG_FATAL("ber_scanf failed to decode password\n");
+            err_msg = "Invalid payload.\n";
+            rc = LDAP_PROTOCOL_ERROR;
+            goto done;
+        }
+        rtag = ber_peek_tag(ber, &tlen);
+    }
+
+    if (rtag == GKREQ_ENCTYPES_TAG) {
+        rtag = ber_scanf(ber, "[{");
+        if (rtag == LBER_ERROR) {
+            LOG_FATAL("ber_scanf failed to decode enctypes\n");
+            err_msg = "Invalid payload.\n";
+            rc = LDAP_PROTOCOL_ERROR;
+            goto done;
+        }
+
+        rtag = ber_peek_tag(ber, &tlen);
+        for (num = 0; rtag == LBER_INTEGER; num++) {
+            if ((num % 10) == 0) {
+                /* allocate space for at least 10 more enctypes */
+                enctypes = realloc(enctypes,
+                                   (num + 10) * sizeof(krb5_key_salt_tuple));
+                if (!enctypes) {
+                    LOG_FATAL("allocation failed\n");
+                    err_msg = "Internal error\n";
+                    rc = LDAP_OPERATIONS_ERROR;
+                    goto done;
+                }
+            }
+
+            rtag = ber_scanf(ber, "i", &enctype);
+            if (rtag == LBER_ERROR) {
+                LOG_FATAL("ber_scanf failed to decode enctype\n");
+                err_msg = "Invalid payload.\n";
+                rc = LDAP_PROTOCOL_ERROR;
+                goto done;
+            }
+
+            enctypes[num].ks_enctype = enctype;
+            enctypes[num].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;
+            rtag = ber_peek_tag(ber, &tlen);
+        }
+    }
+
+    rc = LDAP_SUCCESS;
+
+done:
+    if (rc != LDAP_SUCCESS) {
+        free(password);
+        free(svcname);
+        *_err_msg = err_msg;
+    } else {
+        *_password = password;
+        *_svcname = svcname;
+        *wantold = (getnew == 0);
+        *kenctypes = enctypes;
+        *num_kenctypes = num;
+    }
+    if (ber) ber_free(ber, 1);
+    return rc;
+}
+
+/* Format of getkeytab reply
+ *
+ * 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 GKREP_KVNO_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 1)
+#define GKREP_KEY_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 0)
+#define GKREP_SALT_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 1)
+#define GKREP_S2KPARAMS_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 2)
+#define GKREP_KEYTYPE_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 0)
+#define GKREP_KEYVALUE_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 1)
+#define GKREP_SALTTYPE_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 0)
+#define GKREP_SALTVALUE_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 1)
+
+static int encode_getkeytab_reply(krb5_context krbctx,
+                                  krb5_keyblock *kmkey, int mkvno,
+                                  krb5_key_data *keys, int num_keys,
+                                  struct berval **_bvp)
+{
+    int rc = LDAP_OPERATIONS_ERROR;
+    struct berval *bvp = NULL;
+    BerElement *ber = NULL;
+    ber_int_t kvno;
+    krb5_data plain = { 0 };
+
+    ber = ber_alloc();
+    if (!ber) {
+        LOG_OOM();
+        goto done;
+    }
+
+    /* uses last key kvno */
+    kvno = keys[num_keys-1].key_data_kvno;
+
+    rc = ber_printf(ber, "t[{i{", GKREP_KVNO_TAG, kvno);
+    if (rc == -1) {
+        rc = LDAP_OPERATIONS_ERROR;
+        LOG_FATAL("Failed to initiate key buffer\n");
+        goto done;
+    }
+
+    for (int i = 0; i < num_keys; i++) {
+        krb5_enc_data cipher = { 0 };
+        krb5_int16 plen;
+        void *p;
+
+        /* retrieve plain key */
+        memcpy(&plen, keys[i].key_data_contents[0], 2);
+        cipher.ciphertext.data = (char *)keys[i].key_data_contents[0] + 2;
+        cipher.ciphertext.length = keys[i].key_data_length[0] - 2;
+        cipher.enctype = kmkey->enctype;
+        cipher.kvno = mkvno;
+
+        plain.length = le16toh(plen);
+        p = realloc(plain.data, plain.length);
+        if (!p) {
+            LOG_FATAL("Failed to allocate plain buffer\n");
+            rc = LDAP_OPERATIONS_ERROR;
+            goto done;
+        }
+        plain.data = p;
+
+        rc = krb5_c_decrypt(krbctx, kmkey, 0, 0, &cipher, &plain);
+        if (rc) {
+            LOG_FATAL("Failed to decrypt keys\n");
+            rc = LDAP_OPERATIONS_ERROR;
+            goto done;
+        }
+
+        rc = ber_printf(ber,
+                        "{t[{t[i]t[o]}]",
+                        GKREP_KEY_TAG,
+                            GKREP_KEYTYPE_TAG,
+                                (ber_int_t)keys[i].key_data_type[0],
+                            GKREP_KEYVALUE_TAG,
+                                plain.data, (ber_len_t)plain.length);
+        if (rc == -1) {
+            LOG_FATAL("Failed to encode key data\n");
+            rc = LDAP_OPERATIONS_ERROR;
+            goto done;
+        }
+
+        /* if salt available, add it */
+        if (keys[i].key_data_length[1] != 0) {
+            rc = ber_printf(ber,
+                            "t[{t[i]t[o]}]",
+                            GKREP_SALT_TAG,
+                                GKREP_SALTTYPE_TAG,
+                                    (ber_int_t)keys[i].key_data_type[1],
+                                GKREP_SALTVALUE_TAG,
+                                    keys[i].key_data_contents[1],
+                                    (ber_len_t)keys[i].key_data_length[1]);
+            if (rc == -1) {
+                LOG_FATAL("Failed to encode salt data\n");
+                rc = LDAP_OPERATIONS_ERROR;
+                goto done;
+            }
+        }
+
+        rc = ber_printf(ber, "}");
+        if (rc == -1) {
+            LOG_FATAL("Failed to encode data\n");
+            rc = LDAP_OPERATIONS_ERROR;
+            goto done;
+        }
+    }
+
+    rc = ber_printf(ber, "}}]");
+    if (rc == -1) {
+        LOG_FATAL("Failed to terminate key buffer\n");
+        rc = LDAP_OPERATIONS_ERROR;
+        goto done;
+    }
+
+    rc = ber_flatten(ber, &bvp);
+    if (rc == -1) {
+        LOG_FATAL("Failed to encode key buffer\n");
+        rc = LDAP_OPERATIONS_ERROR;
+        goto done;
+    }
+
+    rc = LDAP_SUCCESS;
+
+done:
+    if (rc != LDAP_SUCCESS) {
+        if (bvp) ber_bvfree(bvp);
+    } else {
+        *_bvp = bvp;
+    }
+    if (ber) ber_free(ber, 1);
+    free(plain.data);
+    return rc;
+}
+
+static int get_decoded_key_data(char *svcname,
+                                krb5_key_data **_keys, int *_num_keys,
+                                int *_mkvno, char **_err_msg)
+{
+    int rc = LDAP_OPERATIONS_ERROR;
+    char *err_msg = NULL;
+    krb5_key_data *keys = NULL;
+    int num_keys = 0;
+    int mkvno = 0;
+    Slapi_Entry *target = NULL;
+    Slapi_Attr *attr;
+    Slapi_Value *keys_value;
+    const struct berval *encoded_keys;
+
+    target = get_entry_by_principal(svcname);
+    if (!target) {
+        err_msg = "PrincipalName disappeared while processing.\n";
+        rc = LDAP_OPERATIONS_ERROR;
+        goto done;
+    }
+
+    rc = slapi_entry_attr_find(target, "krbPrincipalKey", &attr);
+    if (rc) {
+        err_msg = "krbPrincipalKey not found\n";
+        rc = LDAP_NO_SUCH_ATTRIBUTE;
+        goto done;
+    }
+    rc = slapi_attr_first_value(attr, &keys_value);
+    if (rc) {
+        err_msg = "Error retrieving krbPrincipalKey\n";
+        rc = LDAP_OPERATIONS_ERROR;
+        goto done;
+    }
+    encoded_keys = slapi_value_get_berval(keys_value);
+    if (!encoded_keys) {
+        err_msg = "Error retrieving encoded krbPrincipalKey\n";
+        rc = LDAP_OPERATIONS_ERROR;
+        goto done;
+    }
+
+    rc = ber_decode_krb5_key_data(discard_const(encoded_keys),
+                                  &mkvno, &num_keys, &keys);
+    if (rc) {
+        err_msg = "Error retrieving decoded krbPrincipalKey\n";
+        rc = LDAP_OPERATIONS_ERROR;
+        goto done;
+    }
+
+    if (num_keys <= 0) {
+        err_msg = "No krbPrincipalKeys available\n";
+        rc = LDAP_OPERATIONS_ERROR;
+        goto done;
+    }
+
+    rc = LDAP_SUCCESS;
+
+done:
+    if (rc != LDAP_SUCCESS) {
+        if (keys) ipa_krb5_free_key_data(keys, num_keys);
+        *_err_msg = err_msg;
+    } else {
+        *_mkvno = mkvno;
+        *_keys = keys;
+        *_num_keys = num_keys;
+    }
+    if (target) slapi_entry_free(target);
+    return rc;
+}
+
+#define WRITEKEYS_OP_CHECK "ipaProtectedOperation;write_keys"
+#define READKEYS_OP_CHECK "ipaProtectedOperation;read_keys"
+
+/* Password Modify Extended operation plugin function */
+static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg)
+{
+    char *bind_dn = NULL;
+    char *err_msg = NULL;
+    int rc = 0;
+    krb5_context krbctx = NULL;
+    krb5_error_code krberr;
+    struct berval *extop_value = NULL;
+    BerElement *ber = NULL;
+    char *service_name = NULL;
+    char *svcname;
+    Slapi_Entry *target_entry = NULL;
+    bool acl_ok = false;
+    char *password = NULL;
+    int num_kenctypes = 0;
+    krb5_key_salt_tuple *kenctypes = NULL;
+    int mkvno = 0;
+    int num_keys = 0;
+    krb5_key_data *keys = NULL;
+    struct ipapwd_data data = { 0 };
+    Slapi_Value **svals = NULL;
+    struct berval *bvp = NULL;
+    LDAPControl new_ctrl;
+    bool wantold = false;
+
+    /* Get Bind DN */
+    slapi_pblock_get(pb, SLAPI_CONN_DN, &bind_dn);
+
+    /* If the connection is bound anonymously, we must refuse to process
+    * this operation. */
+    if (bind_dn == NULL || *bind_dn == '\0') {
+        /* Refuse the operation because they're bound anonymously */
+        err_msg = "Anonymous Binds are not allowed.\n";
+        rc = LDAP_INSUFFICIENT_ACCESS;
+        goto free_and_return;
+    }
+
+    krberr = krb5_init_context(&krbctx);
+    if (krberr) {
+        LOG_FATAL("krb5_init_context failed\n");
+        rc = LDAP_OPERATIONS_ERROR;
+        goto free_and_return;
+    }
+
+    /* Get the ber value of the extended operation */
+    slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &extop_value);
+    if (!extop_value) {
+        LOG_FATAL("Failed to retrieve extended op value from pblock\n");
+        err_msg = "Failed to retrieve extended operation value\n";
+        rc = LDAP_OPERATIONS_ERROR;
+        goto free_and_return;
+    }
+
+    rc = decode_getkeytab_request(extop_value, &wantold, &service_name,
+                                  &password, &kenctypes, &num_kenctypes,
+                                  &err_msg);
+    if (rc != LDAP_SUCCESS) {
+        goto free_and_return;
+    }
+
+    /* make sure it is a valid name */
+    svcname = check_service_name(krbctx, service_name);
+    if (!svcname) {
+        rc = LDAP_OPERATIONS_ERROR;
+        goto free_and_return;
+    }
+    slapi_ch_free_string(&service_name);
+    service_name = svcname;
+
+    /* check entry */
+
+    /* get Entry by krbPrincipalName */
+    target_entry = get_entry_by_principal(service_name);
+    if (!target_entry) {
+        err_msg = "PrincipalName not found.\n";
+        rc = LDAP_NO_SUCH_OBJECT;
+        goto free_and_return;
+    }
+
+    /* ok access allowed */
+    /* do we need to create new keys ? */
+    if (wantold) { /* requesting to retrieve existing ones */
+
+        /* check if we are allowed to *read* keys */
+        acl_ok = is_allowed_to_access_attr(pb, bind_dn, target_entry,
+                                           READKEYS_OP_CHECK, NULL,
+                                           SLAPI_ACL_READ);
+        if (!acl_ok) {
+            LOG_FATAL("Not allowed to retrieve keytab on [%s]!\n",
+                      service_name);
+            err_msg = "Insufficient access rights\n";
+            rc = LDAP_INSUFFICIENT_ACCESS;
+            goto free_and_return;
+        }
+
+    } else {
+
+        /* check if we are allowed to *write* keys */
+        acl_ok = is_allowed_to_access_attr(pb, bind_dn, target_entry,
+                                           WRITEKEYS_OP_CHECK, NULL,
+                                           SLAPI_ACL_WRITE);
+        if (!acl_ok) {
+            LOG_FATAL("Not allowed to set keytab on [%s]!\n",
+                      service_name);
+            err_msg = "Insufficient access rights\n";
+            rc = LDAP_INSUFFICIENT_ACCESS;
+            goto free_and_return;
+        }
+
+        for (int i = 0; i < num_kenctypes; i++) {
+
+            /* Check if supported */
+            for (int j = 0; j < krbcfg->num_supp_encsalts; j++) {
+                if (kenctypes[i].ks_enctype ==
+                                        krbcfg->supp_encsalts[j].ks_enctype) {
+                    continue;
+                }
+            }
+            /* Unsupported, filter out */
+            for (int j = i; j + 1 < num_kenctypes; j++) {
+                kenctypes[j].ks_enctype = kenctypes[j + 1].ks_enctype;
+                kenctypes[j].ks_salttype = kenctypes[j + 1].ks_salttype;
+            }
+            num_kenctypes--;
+            i--;
+        }
+
+        /* check if we have any left */
+        if (num_kenctypes == 0 && kenctypes != NULL) {
+            LOG_FATAL("keyset filtering rejected all proposed keys\n");
+            err_msg = "All enctypes provided are unsupported";
+            rc = LDAP_UNWILLING_TO_PERFORM;
+            goto free_and_return;
+        }
+
+        /* only target is used, leave everything else NULL,
+         * if password is not provided we want to generate a random key */
+        data.target = target_entry;
+        data.password = password;
+
+        svals = ipapwd_encrypt_encode_key(krbcfg, &data,
+                                          kenctypes ? num_kenctypes :
+                                                krbcfg->num_pref_encsalts,
+                                          kenctypes ? kenctypes :
+                                                krbcfg->pref_encsalts,
+                                          &err_msg);
+        if (!svals) {
+            rc = LDAP_OPERATIONS_ERROR;
+            LOG_FATAL("encrypt_encode_keys failed!\n");
+            err_msg = "Internal error while encrypting keys\n";
+            goto free_and_return;
+        }
+
+        rc = store_new_keys(target_entry, service_name, bind_dn, svals,
+                            &err_msg);
+        if (rc != LDAP_SUCCESS) {
+            goto free_and_return;
+        }
+    }
+
+    rc = get_decoded_key_data(service_name,
+                              &keys, &num_keys, &mkvno, &err_msg);
+    if (rc != LDAP_SUCCESS) {
+        goto free_and_return;
+    }
+
+    rc = encode_getkeytab_reply(krbctx, krbcfg->kmkey, mkvno,
+                                keys, num_keys, &bvp);
+    if (rc != LDAP_SUCCESS) {
+        err_msg = "Internal Error.\n";
+        goto free_and_return;
+    }
+
+    new_ctrl.ldctl_oid = KEYTAB_GET_OID;
+    new_ctrl.ldctl_value = *bvp;
+    new_ctrl.ldctl_iscritical = 0;
+    rc = slapi_pblock_set(pb, SLAPI_ADD_RESCONTROL, &new_ctrl);
+
+free_and_return:
+    if (rc == LDAP_SUCCESS) err_msg = NULL;
+    LOG("%s", err_msg ? err_msg : "success");
+    slapi_send_ldap_result(pb, rc, NULL, err_msg, 0, NULL);
+
+    /* Free anything that we allocated above */
+    if (krbctx) krb5_free_context(krbctx);
+    free(kenctypes);
+    free(service_name);
+    free(password);
+    if (target_entry) slapi_entry_free(target_entry);
+    if (keys) ipa_krb5_free_key_data(keys, num_keys);
+    if (svals) {
+        for (int i = 0; svals[i]; i++) {
+            slapi_value_free(&svals[i]);
+        }
+        free(svals);
+    }
+    if (ber) ber_free(ber, 1);
+    if (bvp) ber_bvfree(bvp);
+
+    return SLAPI_PLUGIN_EXTENDED_SENT_RESULT;
+}
+
 static int ipapwd_extop(Slapi_PBlock *pb)
 {
 	struct ipapwd_krbcfg *krbcfg = NULL;
@@ -1305,6 +1870,11 @@ static int ipapwd_extop(Slapi_PBlock *pb)
 		free_ipapwd_krbcfg(&krbcfg);
 		return ret;
 	}
+	if (strcasecmp(oid, KEYTAB_GET_OID) == 0) {
+		ret = ipapwd_getkeytab(pb, krbcfg);
+		free_ipapwd_krbcfg(&krbcfg);
+		return ret;
+	}
 
 	errMesg = "Request OID does not match supported OIDs.\n";
 	rc = LDAP_OPERATIONS_ERROR;
@@ -1438,6 +2008,7 @@ done:
 static char *ipapwd_oid_list[] = {
 	EXOP_PASSWD_OID,
 	KEYTAB_SET_OID,
+	KEYTAB_GET_OID,
 	NULL
 };
 
diff --git a/install/share/60basev3.ldif b/install/share/60basev3.ldif
index 552045b63d9485ccd3685942b10c3f0e5b6105b6..ff148e97c879d5652b1ce3c9b41443d84d49fe40 100644
--- a/install/share/60basev3.ldif
+++ b/install/share/60basev3.ldif
@@ -46,6 +46,8 @@ attributeTypes: (2.16.840.1.113730.3.8.11.46 NAME 'ipaPermLocation' DESC 'Locati
 attributeTypes: (2.16.840.1.113730.3.8.11.47 NAME 'ipaPermRight' DESC 'IPA permission rights' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v3' )
 attributeTypes: (2.16.840.1.113730.3.8.11.48 NAME 'ipaPermTargetFilter' DESC 'IPA permission target filter' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v3' )
 attributeTypes: (2.16.840.1.113730.3.8.11.49 NAME 'ipaPermTarget' DESC 'IPA permission target' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3' )
+attributeTypes: (2.16.840.1.113730.3.8.11.51 NAME 'ipaAllowedToPerform' DESC 'DNs allowed to perform an operation' SUP distinguishedName X-ORIGIN 'IPA-v3')
+attributeTypes: (2.16.840.1.113730.3.8.11.52 NAME 'ipaProtectedOperation' DESC 'Operation to be protected' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
 objectClasses: (2.16.840.1.113730.3.8.12.1 NAME 'ipaExternalGroup' SUP top STRUCTURAL MUST ( cn ) MAY ( ipaExternalMember $ memberOf $ description $ owner) X-ORIGIN 'IPA v3' )
 objectClasses: (2.16.840.1.113730.3.8.12.2 NAME 'ipaNTUserAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier ) MAY ( ipaNTHash $ ipaNTLogonScript $ ipaNTProfilePath $ ipaNTHomeDirectory $ ipaNTHomeDirectoryDrive ) X-ORIGIN 'IPA v3' )
 objectClasses: (2.16.840.1.113730.3.8.12.3 NAME 'ipaNTGroupAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier ) X-ORIGIN 'IPA v3' )
@@ -64,4 +66,5 @@ objectClasses: (2.16.840.1.113730.3.8.12.17 NAME 'ipaTrustedADDomainRange' SUP i
 objectClasses: (2.16.840.1.113730.3.8.12.19 NAME 'ipaUserAuthTypeClass' SUP top AUXILIARY DESC 'Class for authentication methods definition' MAY ipaUserAuthType X-ORIGIN 'IPA v3')
 objectClasses: (2.16.840.1.113730.3.8.12.20 NAME 'ipaUser' AUXILIARY MUST ( uid ) MAY ( userClass ) X-ORIGIN 'IPA v3' )
 objectClasses: (2.16.840.1.113730.3.8.12.21 NAME 'ipaPermissionV2' DESC 'IPA Permission objectclass, version 2' SUP ipaPermission AUXILIARY MUST ( ipaPermBindRuleType $ ipaPermLocation ) MAY ( ipaPermDefaultAttr $ ipaPermIncludedAttr $ ipaPermExcludedAttr $ ipaPermRight $ ipaPermTargetFilter $ ipaPermTarget ) X-ORIGIN 'IPA v3' )
+objectclasses: (2.16.840.1.113730.3.8.12.22 NAME 'ipaAllowedOperations' SUP top AUXILIARY DESC 'Class to apply access controls to arbitrary operations' MAY ( ipaAllowedToPerform $ ipaProtectedOperation ) X-ORIGIN 'IPA v3')
 objectClasses: (2.16.840.1.113730.3.8.12.23 NAME 'ipaVirtualOperation' DESC 'IPA Virtual operation objectclass' SUP top AUXILIARY MUST ( cn ) X-ORIGIN 'IPA v3' )
diff --git a/install/share/default-aci.ldif b/install/share/default-aci.ldif
index 04fc185f785ee71246c6cc4f958c754158f16302..aab4760fc151ffe01cb2108367d7247c5c0737ea 100644
--- a/install/share/default-aci.ldif
+++ b/install/share/default-aci.ldif
@@ -21,11 +21,17 @@ changetype: modify
 add: aci
 aci: (targetfilter = "(|(objectClass=ipaConfigObject)(dnahostname=*))")(version 3.0;acl "Admins can change GUI config"; allow (delete) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";;)
 
-dn: cn=accounts,$SUFFIX
+dn: cngaccounts,$SUFFIX
 changetype: modify
 add: aci
 aci: (targetattr = "krbMaxPwdLife || krbMinPwdLife || krbPwdMinDiffChars || krbPwdMinLength || krbPwdHistoryLength")(version 3.0;acl "Admins can write password policy"; allow (write) groupdn="ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";;)
 aci: (targetattr = "aci")(version 3.0;acl "Admins can manage delegations"; allow (write, delete) groupdn="ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";;)
+aci: (targetattr="ipaProtectedOperation;read_keys")(version 3.0; acl "Users allowed to retrieve keytab keys"; allow(read) userattr="ipaAllowedToPerform;read_keys#USERDN";)
+aci: (targetattr="ipaProtectedOperation;read_keys")(version 3.0; acl "Groups allowed to retrieve keytab keys"; allow(read) userattr="ipaAllowedToPerform;read_keys#GROUPDN";)
+aci: (targetattr="ipaProtectedOperation;write_keys")(version 3.0; acl "Users allowed to create keytab keys"; allow(write) userattr="ipaAllowedToPerform;write_keys#USERDN";)
+aci: (targetattr="ipaProtectedOperation;write_keys")(version 3.0; acl "Groups allowed to create keytab keys"; allow(write) userattr="ipaAllowedToPerform;write_keys#GROUPDN";)
+aci: (targetattr="ipaProtectedOperation;write_keys")(version 3.0; acl "Entities are allowed to rekey themselves"; allow(write) userdn="ldap:///self";;)
+aci: (targetfilter="(|(objectclass=ipaHost)(objectclass=ipaService))")(targetattr="ipaProtectedOperation;write_keys")(version 3.0; acl "Entities are allowed to rekey managed entries"; allow(write) userattr="managedby#USERDN";)
 
 dn: cn=services,cn=accounts,$SUFFIX
 changetype: modify
diff --git a/install/updates/20-aci.update b/install/updates/20-aci.update
index 6af800111f30d26d37ceb0849f8730a3e15dad0b..09aa62b8669ededb31640cfcfad7e3d853fbd2de 100644
--- a/install/updates/20-aci.update
+++ b/install/updates/20-aci.update
@@ -65,3 +65,12 @@ remove:aci: '(targetattr = "*")(version 3.0; acl "No anonymous access to hbac";
 
 dn: cn=sudo,$SUFFIX
 remove:aci: '(targetattr = "*")(version 3.0; acl "No anonymous access to sudo"; deny (read,search,compare) userdn != "ldap:///all";;)'
+
+# Get Keytab operation Access Control
+dn: cn=accounts,$SUFFIX
+add:aci: (targetattr="ipaProtectedOperation;read_keys")(version 3.0; acl "Users allowed to retrieve keytab keys"; allow(read) userattr="ipaAllowedToPerform;read_keys#USERDN";)
+add:aci: (targetattr="ipaProtectedOperation;read_keys")(version 3.0; acl "Groups allowed to retrieve keytab keys"; allow(read) userattr="ipaAllowedToPerform;read_keys#GROUPDN";)
+add:aci: (targetattr="ipaProtectedOperation;write_keys")(version 3.0; acl "Users allowed to create keytab keys"; allow(write) userattr="ipaAllowedToPerform;write_keys#USERDN";)
+add:aci: (targetattr="ipaProtectedOperation;write_keys")(version 3.0; acl "Groups allowed to create keytab keys"; allow(write) userattr="ipaAllowedToPerform;write_keys#GROUPDN";)
+add:aci: (targetattr="ipaProtectedOperation;write_keys")(version 3.0; acl "Entities are allowed to rekey themselves"; allow(write) userdn="ldap:///self";;)
+add:aci: (targetfilter="(|(objectclass=ipaHost)(objectclass=ipaService))")(targetattr="ipaProtectedOperation;write_keys")(version 3.0; acl "Entities are allowed to rekey managed entries"; allow(write) userattr="managedby#USERDN";)
diff --git a/util/ipa_krb5.h b/util/ipa_krb5.h
index 7fb0355724e3c4f522097df417d2686b85619319..2431fd70b01b86266700e0d30a9cbd2b8f7c7ded 100644
--- a/util/ipa_krb5.h
+++ b/util/ipa_krb5.h
@@ -27,6 +27,7 @@ struct keys_container {
 
 #define KEYTAB_SET_OID "2.16.840.1.113730.3.8.10.1"
 #define KEYTAB_RET_OID "2.16.840.1.113730.3.8.10.2"
+#define KEYTAB_GET_OID "2.16.840.1.113730.3.8.10.5"
 
 void
 ipa_krb5_free_ktypes(krb5_context context, krb5_enctype *val);
-- 
1.9.3

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to