krb5 commit: Add the ability to lock down principal keys

Greg Hudson ghudson at mit.edu
Fri Feb 19 15:47:00 EST 2016


https://github.com/krb5/krb5/commit/534db9834d6a77dc5e948e12844e72ba4e971e8c
commit 534db9834d6a77dc5e948e12844e72ba4e971e8c
Author: Simo Sorce <simo at redhat.com>
Date:   Fri Dec 18 18:13:29 2015 -0500

    Add the ability to lock down principal keys
    
    A new attribute named KRB5_KDC_LOCKDOWN_KEYS can be set on principals.
    This flag prevents keys for the principal from being extracted or set
    to a known value by the kadmin protocol.  Principals with this flag
    cannot be deleted or renamed, and cannot have keys set by setkey or
    chpass.  chrand operations are allowed, but keys are not returned.
    This attribute can be set via the modify operation but cannot be
    reset; an authorization error is resturned if an attempt to reset it
    is performed.
    
    When creating a KDB, set the lockdown flag on the krbtgt and kadmin
    principals.
    
    [ghudson at mit.edu: squash with t_kadmin_acl.py commit; condense commit
    message]
    
    ticket: 8365 (new)

 src/include/kdb.h                           |    1 +
 src/kadmin/cli/kadmin.c                     |    2 +
 src/kadmin/dbutil/kadm5_create.c            |    9 +-
 src/kadmin/dbutil/kdb5_create.c             |    1 +
 src/kadmin/server/server_stubs.c            |  137 +++++++++++++++++++++++++--
 src/lib/kadm5/kadm_err.et                   |    1 +
 src/lib/kadm5/str_conv.c                    |    2 +
 src/tests/dejagnu/krb-standalone/kadmin.exp |   96 ++++++++++++++++++-
 src/tests/t_kadmin_acl.py                   |   40 ++++++++
 src/util/princflags.py                      |    3 +
 10 files changed, 281 insertions(+), 11 deletions(-)

diff --git a/src/include/kdb.h b/src/include/kdb.h
index 71546af..0a9ddbd 100644
--- a/src/include/kdb.h
+++ b/src/include/kdb.h
@@ -98,6 +98,7 @@
 #define KRB5_KDB_OK_AS_DELEGATE         0x00100000
 #define KRB5_KDB_OK_TO_AUTH_AS_DELEGATE 0x00200000 /* S4U2Self OK */
 #define KRB5_KDB_NO_AUTH_DATA_REQUIRED  0x00400000
+#define KRB5_KDB_LOCKDOWN_KEYS          0x00800000
 
 /* Creation flags */
 #define KRB5_KDB_CREATE_BTREE           0x00000001
diff --git a/src/kadmin/cli/kadmin.c b/src/kadmin/cli/kadmin.c
index c9cc0b0..41f172e 100644
--- a/src/kadmin/cli/kadmin.c
+++ b/src/kadmin/cli/kadmin.c
@@ -1105,6 +1105,7 @@ kadmin_addprinc_usage()
             "\t\trequires_hwauth needchange allow_svr "
             "password_changing_service\n"
             "\t\tok_as_delegate ok_to_auth_as_delegate no_auth_data_required\n"
+            "\t\tlockdown_keys\n"
             "\nwhere,\n\t[-x db_princ_args]* - any number of database "
             "specific arguments.\n"
             "\t\t\tLook at each database documentation for supported "
@@ -1127,6 +1128,7 @@ kadmin_modprinc_usage()
             "\t\trequires_hwauth needchange allow_svr "
             "password_changing_service\n"
             "\t\tok_as_delegate ok_to_auth_as_delegate no_auth_data_required\n"
+            "\t\tlockdown_keys\n"
             "\nwhere,\n\t[-x db_princ_args]* - any number of database "
             "specific arguments.\n"
             "\t\t\tLook at each database documentation for supported "
diff --git a/src/kadmin/dbutil/kadm5_create.c b/src/kadmin/dbutil/kadm5_create.c
index 159a419..1213050 100644
--- a/src/kadmin/dbutil/kadm5_create.c
+++ b/src/kadmin/dbutil/kadm5_create.c
@@ -201,20 +201,23 @@ static int add_admin_princs(void *handle, krb5_context context, char *realm)
 
     if ((ret = add_admin_princ(handle, context,
                                service_name, realm,
-                               KRB5_KDB_DISALLOW_TGT_BASED,
+                               KRB5_KDB_DISALLOW_TGT_BASED |
+                               KRB5_KDB_LOCKDOWN_KEYS,
                                ADMIN_LIFETIME)))
         goto clean_and_exit;
 
     if ((ret = add_admin_princ(handle, context,
                                KADM5_ADMIN_SERVICE, realm,
-                               KRB5_KDB_DISALLOW_TGT_BASED,
+                               KRB5_KDB_DISALLOW_TGT_BASED |
+                               KRB5_KDB_LOCKDOWN_KEYS,
                                ADMIN_LIFETIME)))
         goto clean_and_exit;
 
     if ((ret = add_admin_princ(handle, context,
                                KADM5_CHANGEPW_SERVICE, realm,
                                KRB5_KDB_DISALLOW_TGT_BASED |
-                               KRB5_KDB_PWCHANGE_SERVICE,
+                               KRB5_KDB_PWCHANGE_SERVICE |
+                               KRB5_KDB_LOCKDOWN_KEYS,
                                CHANGEPW_LIFETIME)))
         goto clean_and_exit;
 
diff --git a/src/kadmin/dbutil/kdb5_create.c b/src/kadmin/dbutil/kdb5_create.c
index 3698d57..74e506a 100644
--- a/src/kadmin/dbutil/kdb5_create.c
+++ b/src/kadmin/dbutil/kdb5_create.c
@@ -500,6 +500,7 @@ add_principal(context, princ, op, pblock)
     entry->mask = (KADM5_KEY_DATA | KADM5_PRINCIPAL | KADM5_ATTRIBUTES |
                    KADM5_MAX_LIFE | KADM5_MAX_RLIFE | KADM5_TL_DATA |
                    KADM5_PRINC_EXPIRE_TIME);
+    entry->attributes |= KRB5_KDB_LOCKDOWN_KEYS;
 
     retval = krb5_db_put_principal(context, entry);
 
diff --git a/src/kadmin/server/server_stubs.c b/src/kadmin/server/server_stubs.c
index 1126b7b..8d44fff 100644
--- a/src/kadmin/server/server_stubs.c
+++ b/src/kadmin/server/server_stubs.c
@@ -454,6 +454,21 @@ exit_func:
     return &ret;
 }
 
+/* Return KADM5_PROTECT_KEYS if KRB5_KDB_LOCKDOWN_KEYS is set for princ. */
+static kadm5_ret_t
+check_lockdown_keys(kadm5_server_handle_t handle, krb5_principal princ)
+{
+    kadm5_principal_ent_rec rec;
+    kadm5_ret_t ret;
+
+    ret = kadm5_get_principal(handle, princ, &rec, KADM5_ATTRIBUTES);
+    if (ret)
+        return ret;
+    ret = (rec.attributes & KRB5_KDB_LOCKDOWN_KEYS) ? KADM5_PROTECT_KEYS : 0;
+    kadm5_free_principal_ent(handle, &rec);
+    return ret;
+}
+
 generic_ret *
 delete_principal_2_svc(dprinc_arg *arg, struct svc_req *rqstp)
 {
@@ -491,7 +506,17 @@ delete_principal_2_svc(dprinc_arg *arg, struct svc_req *rqstp)
         log_unauth("kadm5_delete_principal", prime_arg,
                    &client_name, &service_name, rqstp);
     } else {
+        ret.code = check_lockdown_keys(handle, arg->princ);
+        if (ret.code == KADM5_PROTECT_KEYS) {
+            log_unauth("kadm5_delete_principal", prime_arg, &client_name,
+                       &service_name, rqstp);
+            ret.code = KADM5_AUTH_DELETE;
+        }
+    }
+
+    if (ret.code == KADM5_OK)
         ret.code = kadm5_delete_principal((void *)handle, arg->princ);
+    if (ret.code != KADM5_AUTH_DELETE) {
         if( ret.code != 0 )
             errmsg = krb5_get_error_message(handle->context, ret.code);
 
@@ -548,7 +573,17 @@ modify_principal_2_svc(mprinc_arg *arg, struct svc_req *rqstp)
         ret.code = KADM5_AUTH_MODIFY;
         log_unauth("kadm5_modify_principal", prime_arg,
                    &client_name, &service_name, rqstp);
-    } else {
+    } else if ((arg->mask & KADM5_ATTRIBUTES) &&
+               (!(arg->rec.attributes & KRB5_KDB_LOCKDOWN_KEYS))) {
+        ret.code = check_lockdown_keys(handle, arg->rec.principal);
+        if (ret.code == KADM5_PROTECT_KEYS) {
+            log_unauth("kadm5_modify_principal", prime_arg, &client_name,
+                       &service_name, rqstp);
+            ret.code = KADM5_AUTH_MODIFY;
+        }
+    }
+
+    if (ret.code == KADM5_OK) {
         ret.code = kadm5_modify_principal((void *)handle, &arg->rec,
                                           arg->mask);
         if( ret.code != 0 )
@@ -621,6 +656,14 @@ rename_principal_2_svc(rprinc_arg *arg, struct svc_req *rqstp)
             else
                 ret.code = KADM5_AUTH_ADD;
         }
+        if (ret.code == KADM5_OK) {
+            ret.code = check_lockdown_keys(handle, arg->src);
+            if (ret.code == KADM5_PROTECT_KEYS) {
+                log_unauth("kadm5_rename_principal", prime_arg1, &client_name,
+                           &service_name, rqstp);
+                ret.code = KADM5_AUTH_DELETE;
+            }
+        }
     } else
         ret.code = KADM5_AUTH_INSUFFICIENT;
     if (ret.code != KADM5_OK) {
@@ -815,7 +858,14 @@ chpass_principal_2_svc(chpass_arg *arg, struct svc_req *rqstp)
         goto exit_func;
     }
 
-    if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) {
+    ret.code = check_lockdown_keys(handle, arg->princ);
+    if (ret.code != KADM5_OK) {
+        if (ret.code == KADM5_PROTECT_KEYS) {
+            log_unauth("kadm5_chpass_principal", prime_arg, &client_name,
+                       &service_name, rqstp);
+            ret.code = KADM5_AUTH_CHANGEPW;
+        }
+    } else if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) {
         ret.code = chpass_principal_wrapper_3((void *)handle, arg->princ,
                                               FALSE, 0, NULL, arg->pass);
     } else if (!(CHANGEPW_SERVICE(rqstp)) &&
@@ -878,7 +928,14 @@ chpass_principal3_2_svc(chpass3_arg *arg, struct svc_req *rqstp)
         goto exit_func;
     }
 
-    if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) {
+    ret.code = check_lockdown_keys(handle, arg->princ);
+    if (ret.code != KADM5_OK) {
+        if (ret.code == KADM5_PROTECT_KEYS) {
+            log_unauth("kadm5_chpass_principal", prime_arg, &client_name,
+                       &service_name, rqstp);
+            ret.code = KADM5_AUTH_CHANGEPW;
+        }
+    } else if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) {
         ret.code = chpass_principal_wrapper_3((void *)handle, arg->princ,
                                               arg->keepold,
                                               arg->n_ks_tuple,
@@ -947,7 +1004,14 @@ setv4key_principal_2_svc(setv4key_arg *arg, struct svc_req *rqstp)
         goto exit_func;
     }
 
-    if (!(CHANGEPW_SERVICE(rqstp)) &&
+    ret.code = check_lockdown_keys(handle, arg->princ);
+    if (ret.code != KADM5_OK) {
+        if (ret.code == KADM5_PROTECT_KEYS) {
+            log_unauth("kadm5_setv4key_principal", prime_arg, &client_name,
+                       &service_name, rqstp);
+            ret.code = KADM5_AUTH_SETKEY;
+        }
+    } else if (!(CHANGEPW_SERVICE(rqstp)) &&
         kadm5int_acl_check(handle->context, rqst2name(rqstp),
                            ACL_SETKEY, arg->princ, NULL)) {
         ret.code = kadm5_setv4key_principal((void *)handle, arg->princ,
@@ -1007,7 +1071,14 @@ setkey_principal_2_svc(setkey_arg *arg, struct svc_req *rqstp)
         goto exit_func;
     }
 
-    if (!(CHANGEPW_SERVICE(rqstp)) &&
+    ret.code = check_lockdown_keys(handle, arg->princ);
+    if (ret.code != KADM5_OK) {
+        if (ret.code == KADM5_PROTECT_KEYS) {
+            log_unauth("kadm5_setkey_principal", prime_arg, &client_name,
+                       &service_name, rqstp);
+            ret.code = KADM5_AUTH_SETKEY;
+        }
+    } else if (!(CHANGEPW_SERVICE(rqstp)) &&
         kadm5int_acl_check(handle->context, rqst2name(rqstp),
                            ACL_SETKEY, arg->princ, NULL)) {
         ret.code = kadm5_setkey_principal((void *)handle, arg->princ,
@@ -1067,7 +1138,14 @@ setkey_principal3_2_svc(setkey3_arg *arg, struct svc_req *rqstp)
         goto exit_func;
     }
 
-    if (!(CHANGEPW_SERVICE(rqstp)) &&
+    ret.code = check_lockdown_keys(handle, arg->princ);
+    if (ret.code != KADM5_OK) {
+        if (ret.code == KADM5_PROTECT_KEYS) {
+            log_unauth("kadm5_setkey_principal", prime_arg, &client_name,
+                       &service_name, rqstp);
+            ret.code = KADM5_AUTH_SETKEY;
+        }
+    } else if (!(CHANGEPW_SERVICE(rqstp)) &&
         kadm5int_acl_check(handle->context, rqst2name(rqstp),
                            ACL_SETKEY, arg->princ, NULL)) {
         ret.code = kadm5_setkey_principal_3((void *)handle, arg->princ,
@@ -1130,7 +1208,14 @@ setkey_principal4_2_svc(setkey4_arg *arg, struct svc_req *rqstp)
         goto exit_func;
     }
 
-    if (!(CHANGEPW_SERVICE(rqstp)) &&
+    ret.code = check_lockdown_keys(handle, arg->princ);
+    if (ret.code != KADM5_OK) {
+        if (ret.code == KADM5_PROTECT_KEYS) {
+            log_unauth("kadm5_setkey_principal", prime_arg, &client_name,
+                       &service_name, rqstp);
+            ret.code = KADM5_AUTH_SETKEY;
+        }
+    } else if (!(CHANGEPW_SERVICE(rqstp)) &&
         kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_SETKEY,
                            arg->princ, NULL)) {
         ret.code = kadm5_setkey_principal_4((void *)handle, arg->princ,
@@ -1161,6 +1246,27 @@ exit_func:
     return &ret;
 }
 
+/* Empty out *keys/*nkeys if princ is protected with the lockdown attribute, or
+ * if we fail to check. */
+static kadm5_ret_t
+chrand_check_lockdown(kadm5_server_handle_t handle, krb5_principal princ,
+                      krb5_keyblock **keys, int *nkeys)
+{
+    kadm5_ret_t ret;
+    int i;
+
+    ret = check_lockdown_keys(handle, princ);
+    if (!ret)
+        return 0;
+
+    for (i = 0; i < *nkeys; i++)
+        krb5_free_keyblock_contents(handle->context, &((*keys)[i]));
+    free(*keys);
+    *keys = NULL;
+    *nkeys = 0;
+    return (ret == KADM5_PROTECT_KEYS) ? KADM5_OK : ret;
+}
+
 chrand_ret *
 chrand_principal_2_svc(chrand_arg *arg, struct svc_req *rqstp)
 {
@@ -1211,6 +1317,7 @@ chrand_principal_2_svc(chrand_arg *arg, struct svc_req *rqstp)
     }
 
     if(ret.code == KADM5_OK) {
+        ret.code = chrand_check_lockdown(handle, arg->princ, &k, &nkeys);
         ret.keys = k;
         ret.n_keys = nkeys;
     }
@@ -1288,6 +1395,7 @@ chrand_principal3_2_svc(chrand3_arg *arg, struct svc_req *rqstp)
     }
 
     if(ret.code == KADM5_OK) {
+        ret.code = chrand_check_lockdown(handle, arg->princ, &k, &nkeys);
         ret.keys = k;
         ret.n_keys = nkeys;
     }
@@ -1920,6 +2028,21 @@ get_principal_keys_2_svc(getpkeys_arg *arg, struct svc_req *rqstp)
         ret.code = KADM5_AUTH_EXTRACT;
     }
 
+    if (ret.code == KADM5_OK) {
+        ret.code = check_lockdown_keys(handle, arg->princ);
+        if (ret.code != KADM5_OK) {
+            kadm5_free_kadm5_key_data(handle->context, ret.n_key_data,
+                                      ret.key_data);
+            ret.key_data = NULL;
+            ret.n_key_data = 0;
+        }
+        if (ret.code == KADM5_PROTECT_KEYS) {
+            log_unauth("kadm5_get_principal_keys", prime_arg,
+                       &client_name, &service_name, rqstp);
+            ret.code = KADM5_AUTH_EXTRACT;
+        }
+    }
+
     if (ret.code != KADM5_AUTH_EXTRACT) {
         if (ret.code != 0)
             errmsg = krb5_get_error_message(handle->context, ret.code);
diff --git a/src/lib/kadm5/kadm_err.et b/src/lib/kadm5/kadm_err.et
index 56a4e27..71b0534 100644
--- a/src/lib/kadm5/kadm_err.et
+++ b/src/lib/kadm5/kadm_err.et
@@ -65,4 +65,5 @@ error_code KADM5_PASS_Q_GENERIC, "Unspecified password quality failure"
 error_code KADM5_BAD_KEYSALTS, "Invalid key/salt tuples"
 error_code KADM5_SETKEY_BAD_KVNO, "Invalid multiple or duplicate kvnos in setkey operation"
 error_code KADM5_AUTH_EXTRACT, "Operation requires ``extract-keys'' privilege"
+error_code KADM5_PROTECT_KEYS, "Principal keys are locked down"
 end
diff --git a/src/lib/kadm5/str_conv.c b/src/lib/kadm5/str_conv.c
index 0441a17..7cf51d3 100644
--- a/src/lib/kadm5/str_conv.c
+++ b/src/lib/kadm5/str_conv.c
@@ -90,6 +90,7 @@ static const struct flag_table_row ftbl[] = {
     {"ok_as_delegate",          KRB5_KDB_OK_AS_DELEGATE,        0},
     {"ok_to_auth_as_delegate",  KRB5_KDB_OK_TO_AUTH_AS_DELEGATE, 0},
     {"no_auth_data_required",   KRB5_KDB_NO_AUTH_DATA_REQUIRED, 0},
+    {"lockdown_keys",           KRB5_KDB_LOCKDOWN_KEYS,         0},
 };
 #define NFTBL (sizeof(ftbl) / sizeof(ftbl[0]))
 
@@ -117,6 +118,7 @@ static const char *outflags[] = {
     "OK_AS_DELEGATE",           /* 0x00100000 */
     "OK_TO_AUTH_AS_DELEGATE",   /* 0x00200000 */
     "NO_AUTH_DATA_REQUIRED",    /* 0x00400000 */
+    "LOCKDOWN_KEYS",            /* 0x00800000 */
 };
 #define NOUTFLAGS (sizeof(outflags) / sizeof(outflags[0]))
 
diff --git a/src/tests/dejagnu/krb-standalone/kadmin.exp b/src/tests/dejagnu/krb-standalone/kadmin.exp
index c62e183..33fc34a 100644
--- a/src/tests/dejagnu/krb-standalone/kadmin.exp
+++ b/src/tests/dejagnu/krb-standalone/kadmin.exp
@@ -600,6 +600,100 @@ proc kadmin_delete { pname } {
 }
 
 #++
+# kadmin_delete	- Test delete principal function of kadmin.
+#
+# Deletes principal $pname.  Returns 1 on success.
+#--
+proc kadmin_delete_locked_down { pname } {
+    global REALMNAME
+    global KADMIN
+    global KADMIN_LOCAL
+    global KEY
+    global spawn_id
+    global tmppwd
+
+    #
+    # First test that we fail, then unlock and retry
+    #
+
+    set good 0
+    spawn $KADMIN -p krbtest/admin@$REALMNAME -q "delprinc -force $pname"
+    expect_after {
+	"Cannot contact any KDC" {
+	    fail "kadmin_delete $pname lost KDC"
+	    catch "expect_after"
+	    return 0
+	}
+	timeout {
+	    fail "kadmin delprinc $pname"
+	    catch "expect_after"
+	    return 0
+	}
+	eof {
+	    fail "kadmin delprinc $pname"
+	    catch "expect_after"
+	    return 0
+	}
+    }
+    expect -re "assword\[^\r\n\]*: *" {
+	send "adminpass$KEY\r"
+    }
+    expect "delete_principal: Operation requires ``delete'' privilege while deleting principal \"$pname@$REALMNAME\"" { set good 1 }
+    expect_after
+    expect eof
+    set k_stat [wait -i $spawn_id]
+    verbose "wait -i $spawn_id returned $k_stat (kadmin delprinc)"
+    catch "close -i $spawn_id"
+    if { $good == 1 } {
+	#
+	# use kadmin.local to remove lockdown.
+	#
+	envstack_push
+	setup_kerberos_env kdc
+	spawn $KADMIN_LOCAL -r $REALMNAME
+	envstack_pop
+	expect_after {
+	    -i $spawn_id
+	    timeout {
+		fail "kadmin delprinc $pname"
+		catch "expect_after"
+		return 0
+	    }
+	    eof {
+		fail "kadmin delprinc $pname"
+		catch "expect_after"
+		return 0
+	    }
+	}
+	set good 0
+	expect "kadmin.local: " { send "modprinc -lockdown_keys $pname\r" }
+	expect "Principal \"$pname@$REALMNAME\" modified." { set good 1 }
+	expect "kadmin.local: " { send "quit\r" }
+	expect_after
+	expect eof
+	set k_stat [wait -i $spawn_id]
+	verbose "wait -i $spawn_id returned $k_stat (kadmin.local show)"
+	catch "close -i $spawn_id"
+	if { $good == 1 } {
+            set good 0
+            if {[kadmin_delete $pname]} { set good 1 }
+        }
+	if { $good == 1 } {
+	    pass "kadmin delprinc $pname"
+	    return 1
+	}
+	else {
+	    fail "kadmin delprinc $pname"
+	    return 0
+	}
+    }
+    else {
+	fail "kadmin delprinc $pname"
+	return 0
+    }
+}
+
+#++
 # kpasswd_cpw	- Test password changing using kpasswd.
 #
 # Change $princ's password from $opw to $npw.  Returns 1 on success.
@@ -1051,7 +1145,7 @@ proc kadmin_test { } {
     }
 
     # test fallback to kadmin/admin
-    if {![kadmin_delete kadmin/$hostname] \
+    if {![kadmin_delete_locked_down kadmin/$hostname] \
 	    || ![kadmin_list] \
 	    || ![kadmin_add_rnd kadmin/$hostname -allow_tgs_req] \
 	    || ![kadmin_list]} {
diff --git a/src/tests/t_kadmin_acl.py b/src/tests/t_kadmin_acl.py
index 6f5c589..188929a 100755
--- a/src/tests/t_kadmin_acl.py
+++ b/src/tests/t_kadmin_acl.py
@@ -1,5 +1,6 @@
 #!/usr/bin/python
 from k5test import *
+import os
 
 realm = K5Realm(create_host=False, create_user=False)
 
@@ -23,6 +24,8 @@ all_inquire = make_client('all_inquire')
 all_list = make_client('all_list')
 all_modify = make_client('all_modify')
 all_rename = make_client('all_rename')
+all_wildcard = make_client('all_wildcard')
+all_extract = make_client('all_extract')
 some_add = make_client('some_add')
 some_changepw = make_client('some_changepw')
 some_delete = make_client('some_delete')
@@ -49,6 +52,8 @@ all_inquire        i
 all_list           l
 all_modify         im
 all_rename         ad
+all_wildcard       x
+all_extract        ie
 some_add           a   selected
 some_changepw      c   selected
 some_delete        d   selected
@@ -318,4 +323,39 @@ out = realm.run([kadminl, 'getprinc', 'type3'])
 if 'Maximum renewable life: 0 days 02:00:00' not in out:
     fail('restriction (maxrenewlife high)')
 
+realm.run([kadminl, 'addprinc', '-pw', 'pw', 'extractkeys'])
+out = kadmin_as(all_wildcard, ['ktadd', '-norandkey', 'extractkeys'],
+                expected_code=1)
+if 'Operation requires ``extract-keys\'\' privilege' not in out:
+    fail('extractkeys failure (all_wildcard)')
+kadmin_as(all_extract, ['ktadd', '-norandkey', 'extractkeys'])
+realm.kinit('extractkeys', flags=['-k'])
+os.remove(realm.keytab)
+
+kadmin_as(all_modify, ['modprinc', '+lockdown_keys', 'extractkeys'])
+out = kadmin_as(all_changepw, ['cpw', '-pw', 'newpw', 'extractkeys'],
+                expected_code=1)
+if 'Operation requires ``change-password\'\' privilege' not in out:
+    fail('extractkeys failure (all_changepw)')
+kadmin_as(all_changepw, ['cpw', '-randkey', 'extractkeys'])
+out = kadmin_as(all_extract, ['ktadd', '-norandkey', 'extractkeys'],
+                expected_code=1)
+if 'Operation requires ``extract-keys\'\' privilege' not in out:
+    fail('extractkeys failure (all_extract)')
+out = kadmin_as(all_delete, ['delprinc', 'extractkeys'], expected_code=1)
+if 'Operation requires ``delete\'\' privilege' not in out:
+    fail('extractkeys failure (all_delete)')
+out = kadmin_as(all_rename, ['renprinc', 'extractkeys', 'renamedprinc'],
+                expected_code=1)
+if 'Operation requires ``delete\'\' privilege' not in out:
+    fail('extractkeys failure (all_rename)')
+out = kadmin_as(all_modify, ['modprinc', '-lockdown_keys', 'extractkeys'],
+                expected_code=1)
+if 'Operation requires ``modify\'\' privilege' not in out:
+    fail('extractkeys failure (all_modify)')
+realm.run([kadminl, 'modprinc', '-lockdown_keys', 'extractkeys'])
+kadmin_as(all_extract, ['ktadd', '-norandkey', 'extractkeys'])
+realm.kinit('extractkeys', flags=['-k'])
+os.remove(realm.keytab)
+
 success('kadmin ACL enforcement')
diff --git a/src/util/princflags.py b/src/util/princflags.py
index 16485c5..f568dd2 100644
--- a/src/util/princflags.py
+++ b/src/util/princflags.py
@@ -26,6 +26,7 @@ KRB5_KDB_NEW_PRINC              = 0x00008000
 KRB5_KDB_OK_AS_DELEGATE         = 0x00100000
 KRB5_KDB_OK_TO_AUTH_AS_DELEGATE = 0x00200000
 KRB5_KDB_NO_AUTH_DATA_REQUIRED  = 0x00400000
+KRB5_KDB_LOCKDOWN_KEYS          = 0x00800000
 
 # Input tables -- list of tuples of the form (name, flag, invert)
 
@@ -47,6 +48,7 @@ _kadmin_pflags = [
     ("ok_as_delegate",          KRB5_KDB_OK_AS_DELEGATE,        False),
     ("ok_to_auth_as_delegate",  KRB5_KDB_OK_TO_AUTH_AS_DELEGATE, False),
     ("no_auth_data_required",   KRB5_KDB_NO_AUTH_DATA_REQUIRED, False),
+    ("lockdown_keys",           KRB5_KDB_LOCKDOWN_KEYS,         False),
 ]
 
 # Input forms from lib/kadm5/str_conv.c
@@ -67,6 +69,7 @@ _strconv_pflags = [
     ("md5",                     KRB5_KDB_SUPPORT_DESMD5,        False),
     ("ok-to-auth-as-delegate",  KRB5_KDB_OK_TO_AUTH_AS_DELEGATE, False),
     ("no-auth-data-required",   KRB5_KDB_NO_AUTH_DATA_REQUIRED, False),
+    ("lockdown-keys",           KRB5_KDB_LOCKDOWN_KEYS,         False),
 ]
 
 # kdb.h symbol prefix


More information about the cvs-krb5 mailing list