diff options
author | Nicolas Williams <nico@cryptonector.com> | 2012-07-18 16:27:35 -0500 |
---|---|---|
committer | Greg Hudson <ghudson@mit.edu> | 2012-07-30 19:11:28 -0400 |
commit | 5829ca2b348974e52a67b553afc7f7491007c33a (patch) | |
tree | 3fdbcdfc56a26445c2f2fce9fb72b6deddb28d0f /src | |
parent | 796366a03ea170efb937913acae36a2083a5329e (diff) | |
download | krb5-5829ca2b348974e52a67b553afc7f7491007c33a.tar.gz krb5-5829ca2b348974e52a67b553afc7f7491007c33a.tar.xz krb5-5829ca2b348974e52a67b553afc7f7491007c33a.zip |
Policy extensions + new policy: allowed ks types
This simply adds KADM5_API_VERSION_4 and various fields to the
policy structures:
- attributes (policy-ish principal attributes)
- max_life (max ticket life)
- max_renewable_life (max ticket renewable life)
- allowed_keysalts (allowed key/salt types)
- TL data (future policy extensions)
Of these only allowed_keysalts is currently implemented.
Some refactoring of TL data handling is also done.
ticket: 7223 (new)
Diffstat (limited to 'src')
33 files changed, 973 insertions, 220 deletions
diff --git a/src/clients/kinit/kinit_kdb.c b/src/clients/kinit/kinit_kdb.c index cc3df04b3..8e949f978 100644 --- a/src/clients/kinit/kinit_kdb.c +++ b/src/clients/kinit/kinit_kdb.c @@ -62,7 +62,7 @@ kinit_kdb_init(krb5_context *pcontext, char *realm) config.realm = realm; retval = kadm5_init(*pcontext, "kinit", NULL /*pass*/, "kinit", &config, - KADM5_STRUCT_VERSION, KADM5_API_VERSION_3, NULL, + KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, NULL, &server_handle); if (retval) return retval; diff --git a/src/include/kdb.h b/src/include/kdb.h index 291a05bb6..2a5d2d5fc 100644 --- a/src/include/kdb.h +++ b/src/include/kdb.h @@ -220,6 +220,13 @@ typedef struct _osa_policy_ent_t { krb5_ui_4 pw_max_fail; /* pwdMaxFailure */ krb5_ui_4 pw_failcnt_interval; /* pwdFailureCountInterval */ krb5_ui_4 pw_lockout_duration; /* pwdLockoutDuration */ + /* Only valid if version > 2 */ + krb5_ui_4 attributes; + krb5_ui_4 max_life; + krb5_ui_4 max_renewable_life; + char * allowed_keysalts; + krb5_int16 n_tl_data; + krb5_tl_data * tl_data; } osa_policy_ent_rec, *osa_policy_ent_t; typedef void (*osa_adb_iter_policy_func) (void *, osa_policy_ent_t); @@ -232,6 +239,8 @@ typedef struct __krb5_key_salt_tuple { #define KRB5_KDB_MAGIC_NUMBER 0xdbdbdbdb #define KRB5_KDB_V1_BASE_LENGTH 38 +#define KRB5_KDB_MAX_ALLOWED_KS_LEN 512 + #define KRB5_TL_LAST_PWD_CHANGE 0x0001 #define KRB5_TL_MOD_PRINC 0x0002 #define KRB5_TL_KADM_DATA 0x0003 @@ -566,6 +575,12 @@ krb5_dbe_delete_tl_data( krb5_context context, krb5_int16 tl_data_type); krb5_error_code +krb5_db_update_tl_data(krb5_context context, + krb5_int16 * n_tl_datap, + krb5_tl_data **tl_datap, + krb5_tl_data * new_tl_data); + +krb5_error_code krb5_dbe_update_tl_data( krb5_context context, krb5_db_entry * entry, krb5_tl_data * new_tl_data); diff --git a/src/kadmin/cli/kadmin.c b/src/kadmin/cli/kadmin.c index bcc01b517..649bbc17e 100644 --- a/src/kadmin/cli/kadmin.c +++ b/src/kadmin/cli/kadmin.c @@ -489,13 +489,13 @@ kadmin_startup(int argc, char *argv[]) "credentials.\n"), princstr); retval = kadm5_init_with_creds(context, princstr, cc, svcname, ¶ms, KADM5_STRUCT_VERSION, - KADM5_API_VERSION_3, db_args, &handle); + KADM5_API_VERSION_4, db_args, &handle); } else if (use_anonymous) { printf(_("Authenticating as principal %s with password; " "anonymous requested.\n"), princstr); retval = kadm5_init_anonymous(context, princstr, svcname, ¶ms, KADM5_STRUCT_VERSION, - KADM5_API_VERSION_3, db_args, &handle); + KADM5_API_VERSION_4, db_args, &handle); } else if (use_keytab) { if (keytab_name) printf(_("Authenticating as principal %s with keytab %s.\n"), @@ -505,13 +505,13 @@ kadmin_startup(int argc, char *argv[]) princstr); retval = kadm5_init_with_skey(context, princstr, keytab_name, svcname, ¶ms, KADM5_STRUCT_VERSION, - KADM5_API_VERSION_3, db_args, &handle); + KADM5_API_VERSION_4, db_args, &handle); } else { printf(_("Authenticating as principal %s with password.\n"), princstr); retval = kadm5_init_with_password(context, princstr, password, svcname, ¶ms, KADM5_STRUCT_VERSION, - KADM5_API_VERSION_3, db_args, + KADM5_API_VERSION_4, db_args, &handle); } if (retval) { @@ -855,14 +855,14 @@ cleanup: } static void -kadmin_free_tl_data(kadm5_principal_ent_t princ) +kadmin_free_tl_data(krb5_int16 *n_tl_datap, krb5_tl_data **tl_datap) { - krb5_tl_data *tl_data = princ->tl_data, *next; - int n_tl_data = princ->n_tl_data; + krb5_tl_data *tl_data = *tl_datap, *next; + int n_tl_data = *n_tl_datap; int i; - princ->n_tl_data = 0; - princ->tl_data = NULL; + *n_tl_datap = 0; + *tl_datap = NULL; for (i = 0; tl_data && (i < n_tl_data); i++) { next = tl_data->tl_data_next; @@ -872,12 +872,12 @@ kadmin_free_tl_data(kadm5_principal_ent_t princ) } } -/* Construct a tl_data element and add it to the tail of princ->tl_data. */ +/* Construct a tl_data element and add it to the tail of *tl_datap. */ static void -add_tl_data(kadm5_principal_ent_t princ, krb5_int16 tl_type, krb5_ui_2 len, - krb5_octet *contents) +add_tl_data(krb5_int16 *n_tl_datap, krb5_tl_data **tl_datap, + krb5_int16 tl_type, krb5_ui_2 len, krb5_octet *contents) { - krb5_tl_data *tl_data, **tlp; + krb5_tl_data *tl_data; krb5_octet *copy; copy = malloc(len); @@ -893,9 +893,9 @@ add_tl_data(kadm5_principal_ent_t princ, krb5_int16 tl_type, krb5_ui_2 len, tl_data->tl_data_contents = copy; tl_data->tl_data_next = NULL; - for (tlp = &princ->tl_data; *tlp != NULL; tlp = &(*tlp)->tl_data_next); - *tlp = tl_data; - princ->n_tl_data++; + for (; *tl_datap != NULL; tl_datap = &(*tl_datap)->tl_data_next); + *tl_datap = tl_data; + (*n_tl_datap)++; } static void @@ -917,7 +917,8 @@ unlock_princ(kadm5_principal_ent_t princ, long *mask, const char *caller) exit(1); } store_32_le((krb5_int32)now, timebuf); - add_tl_data(princ, KRB5_TL_LAST_ADMIN_UNLOCK, 4, timebuf); + add_tl_data(&princ->n_tl_data, &princ->tl_data, + KRB5_TL_LAST_ADMIN_UNLOCK, 4, timebuf); *mask |= KADM5_TL_DATA; } @@ -949,7 +950,8 @@ kadmin_parse_princ_args(int argc, char *argv[], kadm5_principal_ent_t oprinc, if (++i > argc - 2) return -1; - add_tl_data(oprinc, KRB5_TL_DB_ARGS, strlen(argv[i]) + 1, + add_tl_data(&oprinc->n_tl_data, &oprinc->tl_data, + KRB5_TL_DB_ARGS, strlen(argv[i]) + 1, (krb5_octet *)argv[i]); *mask |= KADM5_TL_DATA; continue; @@ -1259,7 +1261,7 @@ cleanup: krb5_free_principal(context, princ.principal); free(ks_tuple); free(canon); - kadmin_free_tl_data(&princ); + kadmin_free_tl_data(&princ.n_tl_data, &princ.tl_data); } void @@ -1323,7 +1325,7 @@ kadmin_modprinc(int argc, char *argv[]) cleanup: krb5_free_principal(context, kprinc); krb5_free_principal(context, princ.principal); - kadmin_free_tl_data(&princ); + kadmin_free_tl_data(&princ.n_tl_data, &princ.tl_data); free(canon); free(ks_tuple); } @@ -1473,6 +1475,7 @@ static int kadmin_parse_policy_args(int argc, char *argv[], kadm5_policy_ent_t policy, long *mask, char *caller) { + krb5_error_code retval; int i; time_t now, date; @@ -1562,6 +1565,25 @@ kadmin_parse_policy_args(int argc, char *argv[], kadm5_policy_ent_t policy, } *mask |= KADM5_PW_LOCKOUT_DURATION; continue; + } else if (!strcmp(argv[i], "-allowedkeysalts")) { + krb5_key_salt_tuple *ks_tuple = NULL; + int n_ks_tuple = 0; + + if (++i > argc - 2) + return -1; + if (strcmp(argv[i], "-")) { + retval = krb5_string_to_keysalts(argv[i], ",", ":.-", 0, + &ks_tuple, &n_ks_tuple); + if (retval) { + com_err(caller, retval, _("while parsing keysalts %s"), + argv[i]); + return -1; + } + free(ks_tuple); + policy->allowed_keysalts = argv[i]; + } + *mask |= KADM5_POLICY_ALLOWED_KEYSALTS; + continue; } else return -1; } @@ -1580,7 +1602,8 @@ kadmin_addmodpol_usage(char *func) fprintf(stderr, _("\t\t[-maxlife time] [-minlife time] [-minlength length]\n" "\t\t[-minclasses number] [-history number]\n" - "\t\t[-maxfailure number] [-failurecountinterval time]\n")); + "\t\t[-maxfailure number] [-failurecountinterval time]\n" + "\t\t[-allowedkeysalts keysalts]\n")); fprintf(stderr, _("\t\t[-lockoutduration time]\n")); } @@ -1683,14 +1706,18 @@ kadmin_getpol(int argc, char *argv[]) strdur(policy.pw_failcnt_interval)); printf(_("Password lockout duration: %s\n"), strdur(policy.pw_lockout_duration)); + if (policy.allowed_keysalts != NULL) + printf(_("Allowed key/salt types: %s\n"), policy.allowed_keysalts); } else { - printf("\"%s\"\t%ld\t%ld\t%ld\t%ld\t%ld\t%ld\t%lu\t%ld\t%ld\n", + printf("\"%s\"\t%ld\t%ld\t%ld\t%ld\t%ld\t%ld\t%lu\t%ld\t%ld\t%s\n", policy.policy, policy.pw_max_life, policy.pw_min_life, policy.pw_min_length, policy.pw_min_classes, policy.pw_history_num, policy.policy_refcnt, (unsigned long)policy.pw_max_fail, (long)policy.pw_failcnt_interval, - (long)policy.pw_lockout_duration); + (long)policy.pw_lockout_duration, + (policy.allowed_keysalts == NULL) ? "-" : + policy.allowed_keysalts); } kadm5_free_policy_ent(handle, &policy); } diff --git a/src/kadmin/dbutil/dump.c b/src/kadmin/dbutil/dump.c index 2d817df26..e421e7659 100644 --- a/src/kadmin/dbutil/dump.c +++ b/src/kadmin/dbutil/dump.c @@ -49,6 +49,10 @@ krb5_kvno new_mkvno; static int backwards; static int recursive; +#define K5Q1(x) #x +#define K5Q(x) K5Q1(x) +#define K5CONST_WIDTH_SCANF_STR(x) "%" K5Q(x) "s" + /* * Use compile(3) if no regcomp present. */ @@ -94,6 +98,7 @@ static krb5_error_code dump_ov_princ (krb5_pointer, krb5_db_entry *); static void dump_k5beta7_policy (void *, osa_policy_ent_t); static void dump_r1_8_policy (void *, osa_policy_ent_t); +static void dump_r1_11_policy (void *, osa_policy_ent_t); typedef krb5_error_code (*dump_func)(krb5_pointer, krb5_db_entry *); @@ -106,6 +111,8 @@ static int process_k5beta7_record (char *, krb5_context, FILE *, int, int *); static int process_r1_8_record (char *, krb5_context, FILE *, int, int *); +static int process_r1_11_record (char *, krb5_context, + FILE *, int, int *); static int process_ov_record (char *, krb5_context, FILE *, int, int *); typedef krb5_error_code (*load_func)(char *, krb5_context, @@ -185,14 +192,23 @@ dump_version r1_8_version = { dump_r1_8_policy, process_r1_8_record, }; +dump_version r1_11_version = { + "Kerberos version 5 release 1.11", + "kdb5_util load_dump version 7\n", + 0, + 0, + dump_k5beta7_princ_withpolicy, + dump_r1_11_policy, + process_r1_11_record, +}; dump_version ipropx_1_version = { "Kerberos iprop extensible version", "ipropx", 0, 0, dump_k5beta7_princ_withpolicy, - dump_r1_8_policy, - process_r1_8_record, + dump_r1_11_policy, + process_r1_11_record, }; /* External data */ @@ -222,6 +238,7 @@ static const char null_mprinc_name[] = "kdb5_dump@MISSING"; #define trash_end_fmt _("%s(%d): ignoring trash at end of line: ") #define read_nomem _("entry (out of memory)") #define read_header _("dump entry header") +#define read_negint _("dump entry (unexpected negative numeric field)") #define read_name_string _("name string") #define read_key_type _("key type") #define read_key_data _("key data") @@ -269,6 +286,7 @@ static const char updateoption[] = "-update"; static const char hashoption[] = "-hash"; static const char ovoption[] = "-ov"; static const char r13option[] = "-r13"; +static const char r18option[] = "-r18"; static const char dump_tmptrail[] = "~"; /* @@ -700,6 +718,33 @@ dump_k5beta6_iterator(ptr, entry) return dump_k5beta6_iterator_ext(ptr, entry, 0); } +/* + * Dumps TL data; common to principals and policies. + * + * If filter_kadm then the KRB5_TL_KADM_DATA (where a principal's policy + * name is stored) is filtered out. This is for dump formats that don't + * support policies. + */ +static void +dump_tl_data(FILE *ofile, krb5_tl_data *tlp, krb5_boolean filter_kadm) +{ + int i; + + for (; tlp; tlp = tlp->tl_data_next) { + if (tlp->tl_data_type == KRB5_TL_KADM_DATA && filter_kadm) + continue; + fprintf(ofile, "\t%d\t%d\t", + (int) tlp->tl_data_type, + (int) tlp->tl_data_length); + if (tlp->tl_data_length) { + for (i = 0; i < tlp->tl_data_length; i++) + fprintf(ofile, "%02x", tlp->tl_data_contents[i]); + } else { + fprintf(ofile, "%d", -1); + } + } +} + static krb5_error_code dump_k5beta6_iterator_ext(ptr, entry, kadm) krb5_pointer ptr; @@ -795,7 +840,7 @@ dump_k5beta6_iterator_ext(ptr, entry, kadm) (int) entry->n_key_data, (int) entry->e_length, name); - fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", + fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d", entry->attributes, entry->max_life, entry->max_renewable_life, @@ -804,21 +849,10 @@ dump_k5beta6_iterator_ext(ptr, entry, kadm) (arg->flags & FLAG_OMIT_NRA) ? 0 : entry->last_success, (arg->flags & FLAG_OMIT_NRA) ? 0 : entry->last_failed, (arg->flags & FLAG_OMIT_NRA) ? 0 : entry->fail_auth_count); - /* Pound out tagged data. */ - for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) { - if (tlp->tl_data_type == KRB5_TL_KADM_DATA && !kadm) - continue; /* see above, [krb5-admin/89] */ - fprintf(arg->ofile, "%d\t%d\t", - (int) tlp->tl_data_type, - (int) tlp->tl_data_length); - if (tlp->tl_data_length) - for (i=0; i<tlp->tl_data_length; i++) - fprintf(arg->ofile, "%02x", tlp->tl_data_contents[i]); - else - fprintf(arg->ofile, "%d", -1); - fprintf(arg->ofile, "\t"); - } + /* Pound out tagged data. */ + dump_tl_data(arg->ofile, entry->tl_data, !kadm); + fprintf(arg->ofile, "\t"); /* Pound out key data */ for (counter=0; counter<entry->n_key_data; counter++) { @@ -950,6 +984,28 @@ void dump_r1_8_policy(void *data, osa_policy_ent_t entry) entry->pw_failcnt_interval, entry->pw_lockout_duration); } +void +dump_r1_11_policy(void *data, osa_policy_ent_t entry) +{ + struct dump_args *arg; + + arg = (struct dump_args *) data; + fprintf(arg->ofile, + "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t" + "%d\t%d\t%d\t%s\t%d", + entry->name, + entry->pw_min_life, entry->pw_max_life, entry->pw_min_length, + entry->pw_min_classes, entry->pw_history_num, + entry->policy_refcnt, entry->pw_max_fail, + entry->pw_failcnt_interval, entry->pw_lockout_duration, + entry->attributes, entry->max_life, entry->max_renewable_life, + entry->allowed_keysalts ? entry->allowed_keysalts : "-", + entry->n_tl_data); + + dump_tl_data(arg->ofile, entry->tl_data, FALSE); + fprintf(arg->ofile, "\n"); +} + static void print_key_data(FILE *f, krb5_key_data *key_data) { int c; @@ -1060,9 +1116,9 @@ static krb5_error_code dump_ov_princ(krb5_pointer ptr, krb5_db_entry *kdb) /* * usage is: - * dump_db [-old] [-b6] [-b7] [-ov] [-r13] [-verbose] [-mkey_convert] - * [-new_mkey_file mkey_file] [-rev] [-recurse] - * [filename [principals...]] + * dump_db [-old] [-b6] [-b7] [-ov] [-r13] [-r18] [-verbose] + * [-mkey_convert] [-new_mkey_file mkey_file] [-rev] + * [-recurse] [filename [principals...]] */ void dump_db(argc, argv) @@ -1085,7 +1141,7 @@ dump_db(argc, argv) * Parse the arguments. */ ofile = (char *) NULL; - dump = &r1_8_version; + dump = &r1_11_version; arglist.flags = 0; new_mkey_file = 0; mkey_convert = 0; @@ -1107,6 +1163,8 @@ dump_db(argc, argv) dump = &ov_version; else if (!strcmp(argv[aindex], r13option)) dump = &r1_3_version; + else if (!strcmp(argv[aindex], r18option)) + dump = &r1_8_version; else if (!strncmp(argv[aindex], ipropoption, sizeof(ipropoption) - 1)) { if (log_ctx && log_ctx->iproprole) { /* Note: ipropx_version is the maximum version acceptable */ @@ -1792,6 +1850,65 @@ process_k5beta_record(fname, kcontext, filep, flags, linenop) return(retval); } +/* Allocate and form a TL data list of a desired size. */ +static int +alloc_tl_data(krb5_int16 n_tl_data, krb5_tl_data **tldp) +{ + krb5_tl_data **tlp = tldp; + int i; + + for (i = 0; i < n_tl_data; i++) { + *tlp = calloc(1, sizeof(krb5_tl_data)); + if (*tlp == NULL) + return ENOMEM; /* caller cleans up */ + tlp = &((*tlp)->tl_data_next); + } + + return 0; +} + +/* Read TL data; common to principals and policies */ +static int +process_tl_data(const char *fname, FILE *filep, krb5_tl_data *tl_data, + const char **errstr) +{ + krb5_tl_data *tl; + int nread; + krb5_int32 t1, t2; + + for (tl = tl_data; tl; tl = tl->tl_data_next) { + nread = fscanf(filep, "%d\t%d\t", &t1, &t2); + if (nread != 2) { + *errstr = read_ttypelen; + return EINVAL; + } + if (t2 < 0) { + *errstr = read_negint; + return EINVAL; + } + tl->tl_data_type = (krb5_int16) t1; + tl->tl_data_length = (krb5_int16) t2; + if (tl->tl_data_length) { + tl->tl_data_contents = malloc(t2 + 1); + if (tl->tl_data_contents == NULL) + return ENOMEM; + if (read_octet_string(filep, tl->tl_data_contents, + tl->tl_data_length)) { + *errstr = read_tcontents; + return EINVAL; + } + } else { + nread = fscanf(filep, "%d", &t1); + if (nread != 1 || t1 != -1) { + *errstr = read_tcontents; + return EINVAL; + } + } + } + + return 0; +} + /* * process_k5beta6_record() - Handle a dump record in krb5b6 format. * @@ -1805,11 +1922,10 @@ process_k5beta6_record(char *fname, krb5_context kcontext, FILE *filep, krb5_db_entry *dbentry; krb5_int32 t1, t2, t3, t4, t5, t6, t7, t8, t9; int nread; - int error = 1; int i, j; char *name; krb5_key_data *kp, *kdatap; - krb5_tl_data **tlp, *tl; + krb5_tl_data *tl; krb5_octet *op; krb5_error_code kret; const char *try2read = read_header; @@ -1824,7 +1940,6 @@ process_k5beta6_record(char *fname, krb5_context kcontext, FILE *filep, op = NULL; nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t", &t1, &t2, &t3, &t4, &t5); if (nread == EOF) { - error = 0; retval = -1; goto cleanup; } @@ -1836,14 +1951,9 @@ process_k5beta6_record(char *fname, krb5_context kcontext, FILE *filep, goto cleanup; /* Get memory for and form tagged data linked list */ - tlp = &dbentry->tl_data; - for (i = 0; i < t3; i++) { - if (!(*tlp = malloc(sizeof(krb5_tl_data)))) - goto cleanup; - memset(*tlp, 0, sizeof(krb5_tl_data)); - tlp = &((*tlp)->tl_data_next); - dbentry->n_tl_data++; - } + if (alloc_tl_data(t3, &dbentry->tl_data)) + goto cleanup; + dbentry->n_tl_data = t3; /* Get memory for key list */ if (t4 && (kp = malloc(t4*sizeof(krb5_key_data))) == NULL) @@ -1911,31 +2021,11 @@ process_k5beta6_record(char *fname, krb5_context kcontext, FILE *filep, * that's what I did. [krb5-admin/89] */ if (dbentry->n_tl_data) { + if (process_tl_data(fname, filep, dbentry->tl_data, &try2read)) + goto cleanup; for (tl = dbentry->tl_data; tl; tl = tl->tl_data_next) { - nread = fscanf(filep, "%d\t%d\t", &t1, &t2); - if (nread != 2) { - try2read = read_ttypelen; - goto cleanup; - } - tl->tl_data_type = (krb5_int16) t1; - tl->tl_data_length = (krb5_int16) t2; - if (!tl->tl_data_length) { - /* Should be a null field */ - nread = fscanf(filep, "%d", &t9); - if ((nread != 1) || (t9 != -1)) { - try2read = read_tcontents; - goto cleanup; - } - continue; - } - if (!(tl->tl_data_contents = malloc(t2 + 1)) || - read_octet_string(filep, tl->tl_data_contents, t2)) { - try2read = read_nomem; - goto cleanup; - } - /* test to set mask fields */ - if (t1 == KRB5_TL_KADM_DATA) { + if (tl->tl_data_type == KRB5_TL_KADM_DATA) { XDR xdrs; osa_princ_ent_rec osa_princ_ent; @@ -2032,10 +2122,9 @@ process_k5beta6_record(char *fname, krb5_context kcontext, FILE *filep, if (flags & FLAG_VERBOSE) fprintf(stderr, add_princ_fmt, name); retval = 0; - error = 0; cleanup: - if (error) + if (retval > 0) fprintf(stderr, read_err_fmt, fname, *linenop, try2read); free(op); @@ -2110,7 +2199,7 @@ process_r1_8_policy(fname, kcontext, filep, flags, linenop) * To make this compatible with future policy extensions, we * ignore any additional values. */ - nread = fscanf(filep, "%1024s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d", + nread = fscanf(filep, "%1024s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d%*[^\n]", rec.name, &rec.pw_min_life, &rec.pw_max_life, &rec.pw_min_length, &rec.pw_min_classes, @@ -2139,6 +2228,83 @@ process_r1_8_policy(fname, kcontext, filep, flags, linenop) return 0; } +static int +process_r1_11_policy(char *fname, krb5_context kcontext, FILE *filep, + int flags, int *linenop) +{ + osa_policy_ent_rec rec; + krb5_tl_data *tl, *tl_next; + char namebuf[1024]; + char keysaltbuf[KRB5_KDB_MAX_ALLOWED_KS_LEN + 1]; + int nread; + int ret = 0; + const char *try2read = NULL; + + memset(&rec, 0, sizeof(rec)); + + (*linenop)++; + rec.name = namebuf; + rec.allowed_keysalts = keysaltbuf; + + nread = fscanf(filep, + "%1023s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t" + "%d\t%d\t%d\t" + K5CONST_WIDTH_SCANF_STR(KRB5_KDB_MAX_ALLOWED_KS_LEN) + "\t%hd", + rec.name, + &rec.pw_min_life, &rec.pw_max_life, + &rec.pw_min_length, &rec.pw_min_classes, + &rec.pw_history_num, &rec.policy_refcnt, + &rec.pw_max_fail, &rec.pw_failcnt_interval, + &rec.pw_lockout_duration, + &rec.attributes, &rec.max_life, &rec.max_renewable_life, + rec.allowed_keysalts, &rec.n_tl_data); + if (nread == EOF) + return -1; + else if (nread != 15) { + fprintf(stderr, "cannot parse policy on line %d (%d read)\n", + *linenop, nread); + return 1; + } + + if (rec.allowed_keysalts && !strcmp(rec.allowed_keysalts, "-")) + rec.allowed_keysalts = NULL; + + /* Get TL data */ + ret = alloc_tl_data(rec.n_tl_data, &rec.tl_data); + if (ret) + goto cleanup; + + ret = process_tl_data(fname, filep, rec.tl_data, &try2read); + if (ret) + goto cleanup; + + if ((ret = krb5_db_create_policy(kcontext, &rec)) && + (ret = krb5_db_put_policy(kcontext, &rec))) { + fprintf(stderr, "cannot create policy on line %d: %s\n", + *linenop, error_message(ret)); + try2read = NULL; + goto cleanup; + } + if (flags & FLAG_VERBOSE) + fprintf(stderr, "created policy %s\n", rec.name); + +cleanup: + for (tl = rec.tl_data; tl; tl = tl_next) { + tl_next = tl->tl_data_next; + free(tl->tl_data_contents); + free(tl); + } + if (ret == ENOMEM) + try2read = no_mem_fmt; + if (ret) { + if (try2read) + fprintf(stderr, read_err_fmt, fname, *linenop, try2read); + return 1; + } + return 0; +} + /* * process_k5beta7_record() - Handle a dump record in krb5b7 format. * @@ -2250,6 +2416,36 @@ process_r1_8_record(fname, kcontext, filep, flags, linenop) } /* + * process_r1_11_record() - Handle a dump record in krb5 1.11 format. + * + * Returns -1 for end of file, 0 for success and 1 for failure. + */ +static int +process_r1_11_record(char *fname, krb5_context kcontext, FILE *filep, + int flags, int *linenop) +{ + int nread; + char rectype[100]; + + nread = fscanf(filep, "%100s\t", rectype); + if (nread == EOF) + return -1; + else if (nread != 1) + return 1; + if (!strcmp(rectype, "princ")) + process_k5beta6_record(fname, kcontext, filep, flags, linenop); + else if (!strcmp(rectype, "policy")) + process_r1_11_policy(fname, kcontext, filep, flags, linenop); + else { + fprintf(stderr, _("unknown record type \"%s\" on line %d\n"), + rectype, *linenop); + return 1; + } + + return 0; +} + +/* * restore_dump() - Restore the database from any version dump file. */ static int @@ -2333,6 +2529,8 @@ load_db(argc, argv) load = &ov_version; else if (!strcmp(argv[aindex], r13option)) load = &r1_3_version; + else if (!strcmp(argv[aindex], r18option)) + load = &r1_8_version; else if (!strcmp(argv[aindex], ipropoption)) { if (log_ctx && log_ctx->iproprole) { load = &iprop_version; @@ -2426,6 +2624,8 @@ load_db(argc, argv) load = &r1_3_version; else if (strcmp(buf, r1_8_version.header) == 0) load = &r1_8_version; + else if (strcmp(buf, r1_11_version.header) == 0) + load = &r1_11_version; else if (strncmp(buf, ov_version.header, strlen(ov_version.header)) == 0) load = &ov_version; diff --git a/src/kadmin/dbutil/kadm5_create.c b/src/kadmin/dbutil/kadm5_create.c index 9d5ee1da4..567f5f5dc 100644 --- a/src/kadmin/dbutil/kadm5_create.c +++ b/src/kadmin/dbutil/kadm5_create.c @@ -108,7 +108,7 @@ int kadm5_create_magic_princs(kadm5_config_params *params, return retval; if ((retval = kadm5_init(context, progname, NULL, NULL, params, KADM5_STRUCT_VERSION, - KADM5_API_VERSION_3, + KADM5_API_VERSION_4, db5util_db_args, &handle))) { com_err(progname, retval, _("while initializing the Kerberos admin " diff --git a/src/kadmin/dbutil/kdb5_util.c b/src/kadmin/dbutil/kdb5_util.c index ca1cdd294..f12c6853b 100644 --- a/src/kadmin/dbutil/kdb5_util.c +++ b/src/kadmin/dbutil/kdb5_util.c @@ -85,10 +85,10 @@ void usage() "\tcreate [-s]\n" "\tdestroy [-f]\n" "\tstash [-f keyfile]\n" - "\tdump [-old|-ov|-b6|-b7|-r13] [-verbose]\n" + "\tdump [-old|-ov|-b6|-b7|-r13|-r18] [-verbose]\n" "\t [-mkey_convert] [-new_mkey_file mkey_file]\n" "\t [-rev] [-recurse] [filename [princs...]]\n" - "\tload [-old|-ov|-b6|-b7|-r13] [-verbose] [-update] " + "\tload [-old|-ov|-b6|-b7|-r13|-r18] [-verbose] [-update] " "filename\n" "\tark [-e etype_list] principal\n" "\tadd_mkey [-e etype] [-s]\n" diff --git a/src/kadmin/server/ovsec_kadmd.c b/src/kadmin/server/ovsec_kadmd.c index dbb90cb20..b77e76561 100644 --- a/src/kadmin/server/ovsec_kadmd.c +++ b/src/kadmin/server/ovsec_kadmd.c @@ -318,7 +318,7 @@ int main(int argc, char *argv[]) if((ret = kadm5_init(context, "kadmind", NULL, NULL, ¶ms, KADM5_STRUCT_VERSION, - KADM5_API_VERSION_3, + KADM5_API_VERSION_4, db_args, &global_server_handle)) != KADM5_OK) { const char *e_txt = krb5_get_error_message (context, ret); diff --git a/src/kadmin/testing/util/tcl_kadm5.c b/src/kadmin/testing/util/tcl_kadm5.c index 0d9c7f11d..8338cc9b3 100644 --- a/src/kadmin/testing/util/tcl_kadm5.c +++ b/src/kadmin/testing/util/tcl_kadm5.c @@ -2528,6 +2528,8 @@ void Tcl_kadm5_init(Tcl_Interp *interp) Tcl_SetVar(interp, "KADM5_API_VERSION_2", buf, TCL_GLOBAL_ONLY); (void) sprintf(buf, "%d", KADM5_API_VERSION_3); Tcl_SetVar(interp, "KADM5_API_VERSION_3", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", KADM5_API_VERSION_4); + Tcl_SetVar(interp, "KADM5_API_VERSION_4", buf, TCL_GLOBAL_ONLY); (void) sprintf(buf, "%d", KADM5_API_VERSION_MASK); Tcl_SetVar(interp, "KADM5_API_VERSION_MASK", buf, TCL_GLOBAL_ONLY); (void) sprintf(buf, "%d", KADM5_STRUCT_VERSION_MASK); diff --git a/src/lib/kadm5/admin.h b/src/lib/kadm5/admin.h index 020962b09..037e2f96e 100644 --- a/src/lib/kadm5/admin.h +++ b/src/lib/kadm5/admin.h @@ -116,15 +116,20 @@ typedef long kadm5_ret_t; /* kadm5_policy_ent_t */ -#define KADM5_PW_MAX_LIFE 0x004000 -#define KADM5_PW_MIN_LIFE 0x008000 -#define KADM5_PW_MIN_LENGTH 0x010000 -#define KADM5_PW_MIN_CLASSES 0x020000 -#define KADM5_PW_HISTORY_NUM 0x040000 -#define KADM5_REF_COUNT 0x080000 -#define KADM5_PW_MAX_FAILURE 0x100000 -#define KADM5_PW_FAILURE_COUNT_INTERVAL 0x200000 -#define KADM5_PW_LOCKOUT_DURATION 0x400000 +#define KADM5_PW_MAX_LIFE 0x00004000 +#define KADM5_PW_MIN_LIFE 0x00008000 +#define KADM5_PW_MIN_LENGTH 0x00010000 +#define KADM5_PW_MIN_CLASSES 0x00020000 +#define KADM5_PW_HISTORY_NUM 0x00040000 +#define KADM5_REF_COUNT 0x00080000 +#define KADM5_PW_MAX_FAILURE 0x00100000 +#define KADM5_PW_FAILURE_COUNT_INTERVAL 0x00200000 +#define KADM5_PW_LOCKOUT_DURATION 0x00400000 +#define KADM5_POLICY_ATTRIBUTES 0x00800000 +#define KADM5_POLICY_MAX_LIFE 0x01000000 +#define KADM5_POLICY_MAX_RLIFE 0x02000000 +#define KADM5_POLICY_ALLOWED_KEYSALTS 0x04000000 +#define KADM5_POLICY_TL_DATA 0x08000000 /* kadm5_config_params */ #define KADM5_CONFIG_REALM 0x00000001 @@ -179,6 +184,7 @@ typedef long kadm5_ret_t; #define KADM5_API_VERSION_MASK 0x12345700 #define KADM5_API_VERSION_2 (KADM5_API_VERSION_MASK|0x02) #define KADM5_API_VERSION_3 (KADM5_API_VERSION_MASK|0x03) +#define KADM5_API_VERSION_4 (KADM5_API_VERSION_MASK|0x04) typedef struct _kadm5_principal_ent_t { krb5_principal principal; @@ -218,6 +224,14 @@ typedef struct _kadm5_policy_ent_t { krb5_kvno pw_max_fail; krb5_deltat pw_failcnt_interval; krb5_deltat pw_lockout_duration; + + /* version 4 fields */ + krb5_flags attributes; + krb5_deltat max_life; + krb5_deltat max_renewable_life; + char *allowed_keysalts; + krb5_int16 n_tl_data; + krb5_tl_data *tl_data; } kadm5_policy_ent_rec, *kadm5_policy_ent_t; /* diff --git a/src/lib/kadm5/admin_internal.h b/src/lib/kadm5/admin_internal.h index dc21a65b3..6d79243bd 100644 --- a/src/lib/kadm5/admin_internal.h +++ b/src/lib/kadm5/admin_internal.h @@ -32,7 +32,7 @@ return KADM5_BAD_API_VERSION; \ if (srvr->api_version < KADM5_API_VERSION_2) \ return old_api_version; \ - if (srvr->api_version > KADM5_API_VERSION_3) \ + if (srvr->api_version > KADM5_API_VERSION_4) \ return new_api_version; \ } diff --git a/src/lib/kadm5/clnt/client_init.c b/src/lib/kadm5/clnt/client_init.c index a8abebfcf..adc050c95 100644 --- a/src/lib/kadm5/clnt/client_init.c +++ b/src/lib/kadm5/clnt/client_init.c @@ -197,7 +197,7 @@ init_any(krb5_context context, char *client_name, enum init_type init_type, handle->destroy_cache = 0; handle->context = 0; *handle->lhandle = *handle; - handle->lhandle->api_version = KADM5_API_VERSION_3; + handle->lhandle->api_version = KADM5_API_VERSION_4; handle->lhandle->struct_version = KADM5_STRUCT_VERSION; handle->lhandle->lhandle = handle->lhandle; @@ -337,6 +337,16 @@ init_any(krb5_context context, char *client_name, enum init_type init_type, #endif goto error; } + /* Drop down to v3 wire protocol if server does not support v4 */ + if (r->code == KADM5_NEW_SERVER_API_VERSION && + handle->api_version == KADM5_API_VERSION_4) { + handle->api_version = KADM5_API_VERSION_3; + r = init_2(&handle->api_version, handle->clnt); + if (r == NULL) { + code = KADM5_RPC_ERROR; + goto error; + } + } /* Drop down to v2 wire protocol if server does not support v3 */ if (r->code == KADM5_NEW_SERVER_API_VERSION && handle->api_version == KADM5_API_VERSION_3) { diff --git a/src/lib/kadm5/clnt/clnt_policy.c b/src/lib/kadm5/clnt/clnt_policy.c index eda354a94..3b3823fde 100644 --- a/src/lib/kadm5/clnt/clnt_policy.c +++ b/src/lib/kadm5/clnt/clnt_policy.c @@ -89,6 +89,8 @@ kadm5_get_policy(void *server_handle, char *name, kadm5_policy_ent_t ent) gpol_ret *r; kadm5_server_handle_t handle = server_handle; + memset(ent, 0, sizeof(*ent)); + CHECK_HANDLE(server_handle); arg.name = name; diff --git a/src/lib/kadm5/kadm_err.et b/src/lib/kadm5/kadm_err.et index 5530ccafa..c71767013 100644 --- a/src/lib/kadm5/kadm_err.et +++ b/src/lib/kadm5/kadm_err.et @@ -62,4 +62,5 @@ error_code KADM5_MISSING_KRB5_CONF_PARAMS, "Missing parameters in krb5.conf requ error_code KADM5_XDR_FAILURE, "XDR encoding error" error_code KADM5_CANT_RESOLVE, "Cannot resolve network address for admin server in requested realm" error_code KADM5_PASS_Q_GENERIC, "Unspecified password quality failure" +error_code KADM5_BAD_KEYSALTS, "Invalid key/salt tuples" end diff --git a/src/lib/kadm5/kadm_rpc_xdr.c b/src/lib/kadm5/kadm_rpc_xdr.c index 0b0253a5c..153b96297 100644 --- a/src/lib/kadm5/kadm_rpc_xdr.c +++ b/src/lib/kadm5/kadm_rpc_xdr.c @@ -492,17 +492,45 @@ _xdr_kadm5_policy_ent_rec(XDR *xdrs, kadm5_policy_ent_rec *objp, int vers) if (!xdr_long(xdrs, &objp->policy_refcnt)) { return (FALSE); } - if (vers == KADM5_API_VERSION_3) { + if (xdrs->x_op == XDR_DECODE) { + objp->pw_max_fail = 0; + objp->pw_failcnt_interval = 0; + objp->pw_lockout_duration = 0; + objp->attributes = 0; + objp->max_life = 0; + objp->max_renewable_life = 0; + objp->allowed_keysalts = NULL; + objp->n_tl_data = 0; + objp->tl_data = NULL; + } + if (vers >= KADM5_API_VERSION_3) { if (!xdr_krb5_kvno(xdrs, &objp->pw_max_fail)) return (FALSE); if (!xdr_krb5_deltat(xdrs, &objp->pw_failcnt_interval)) return (FALSE); if (!xdr_krb5_deltat(xdrs, &objp->pw_lockout_duration)) return (FALSE); - } else if (xdrs->x_op == XDR_DECODE) { - objp->pw_max_fail = 0; - objp->pw_failcnt_interval = 0; - objp->pw_lockout_duration = 0; + } + if (vers >= KADM5_API_VERSION_4) { + if (!xdr_krb5_flags(xdrs, &objp->attributes)) { + return (FALSE); + } + if (!xdr_krb5_deltat(xdrs, &objp->max_life)) { + return (FALSE); + } + if (!xdr_krb5_deltat(xdrs, &objp->max_renewable_life)) { + return (FALSE); + } + if (!xdr_nullstring(xdrs, &objp->allowed_keysalts)) { + return (FALSE); + } + if (!xdr_krb5_int16(xdrs, &objp->n_tl_data)) { + return (FALSE); + } + if (!xdr_nulltype(xdrs, (void **) &objp->tl_data, + xdr_krb5_tl_data)) { + return FALSE; + } } return (TRUE); } @@ -510,7 +538,7 @@ _xdr_kadm5_policy_ent_rec(XDR *xdrs, kadm5_policy_ent_rec *objp, int vers) bool_t xdr_kadm5_policy_ent_rec(XDR *xdrs, kadm5_policy_ent_rec *objp) { - return _xdr_kadm5_policy_ent_rec(xdrs, objp, KADM5_API_VERSION_3); + return _xdr_kadm5_policy_ent_rec(xdrs, objp, KADM5_API_VERSION_4); } bool_t diff --git a/src/lib/kadm5/misc_free.c b/src/lib/kadm5/misc_free.c index 1d7079199..497b8c2b6 100644 --- a/src/lib/kadm5/misc_free.c +++ b/src/lib/kadm5/misc_free.c @@ -11,10 +11,21 @@ kadm5_ret_t kadm5_free_policy_ent(void *server_handle, kadm5_policy_ent_t val) { + krb5_tl_data *tl_next; + _KADM5_CHECK_HANDLE(server_handle); - if (val) - free(val->policy); + if (val == NULL) + return KADM5_OK; + + free(val->policy); + free(val->allowed_keysalts); + for (; val->tl_data; val->tl_data = tl_next) { + tl_next = val->tl_data->tl_data_next; + free(val->tl_data->tl_data_contents); + free(val->tl_data); + } + memset(val, 0, sizeof(*val)); return KADM5_OK; } diff --git a/src/lib/kadm5/srv/server_init.c b/src/lib/kadm5/srv/server_init.c index b170e9e7c..3c3a8795f 100644 --- a/src/lib/kadm5/srv/server_init.c +++ b/src/lib/kadm5/srv/server_init.c @@ -283,7 +283,7 @@ kadm5_ret_t kadm5_init(krb5_context context, char *client_name, char *pass, return ENOMEM; } *handle->lhandle = *handle; - handle->lhandle->api_version = KADM5_API_VERSION_3; + handle->lhandle->api_version = KADM5_API_VERSION_4; handle->lhandle->struct_version = KADM5_STRUCT_VERSION; handle->lhandle->lhandle = handle->lhandle; diff --git a/src/lib/kadm5/srv/svr_policy.c b/src/lib/kadm5/srv/svr_policy.c index 4e0065c3d..3a8f82ecf 100644 --- a/src/lib/kadm5/srv/svr_policy.c +++ b/src/lib/kadm5/srv/svr_policy.c @@ -52,6 +52,24 @@ kadm5_create_policy(void *server_handle, return kadm5_create_policy_internal(server_handle, entry, mask); } +/* Validate allowed_keysalts. */ +static kadm5_ret_t +validate_allowed_keysalts(char *allowed_keysalts) +{ + kadm5_ret_t ret; + krb5_key_salt_tuple *ks_tuple = NULL; + krb5_int32 n_ks_tuple = 0; + + if (strchr(allowed_keysalts, '\t') != NULL) + return KADM5_BAD_KEYSALTS; + ret = krb5_string_to_keysalts(allowed_keysalts, ",", ":.-", 0, + &ks_tuple, &n_ks_tuple); + free(ks_tuple); + if (ret == EINVAL) + return KADM5_BAD_KEYSALTS; + return ret; +} + /* * Function: kadm5_create_policy_internal * @@ -89,7 +107,14 @@ kadm5_create_policy_internal(void *server_handle, return KADM5_BAD_POLICY; if (!(mask & KADM5_POLICY)) return KADM5_BAD_MASK; + if ((mask & KADM5_POLICY_ALLOWED_KEYSALTS) && + entry->allowed_keysalts != NULL) { + ret = validate_allowed_keysalts(entry->allowed_keysalts); + if (ret) + return ret; + } + memset(&pent, 0, sizeof(pent)); pent.name = entry->policy; p = entry->policy; while(*p != '\0') { @@ -138,7 +163,32 @@ kadm5_create_policy_internal(void *server_handle, else pent.policy_refcnt = entry->policy_refcnt; - if (handle->api_version == KADM5_API_VERSION_3) { + if (handle->api_version >= KADM5_API_VERSION_4) { + if (!(mask & KADM5_POLICY_ATTRIBUTES)) + pent.attributes = 0; + else + pent.attributes = entry->attributes; + if (!(mask & KADM5_POLICY_MAX_LIFE)) + pent.max_life = 0; + else + pent.max_life = entry->max_life; + if (!(mask & KADM5_POLICY_MAX_RLIFE)) + pent.max_renewable_life = 0; + else + pent.max_renewable_life = entry->max_renewable_life; + if (!(mask & KADM5_POLICY_ALLOWED_KEYSALTS)) + pent.allowed_keysalts = 0; + else + pent.allowed_keysalts = entry->allowed_keysalts; + if (!(mask & KADM5_POLICY_TL_DATA)) { + pent.n_tl_data = 0; + pent.tl_data = NULL; + } else { + pent.n_tl_data = entry->n_tl_data; + pent.tl_data = entry->tl_data; + } + } + if (handle->api_version >= KADM5_API_VERSION_3) { if (!(mask & KADM5_PW_MAX_FAILURE)) pent.pw_max_fail = 0; else @@ -151,10 +201,6 @@ kadm5_create_policy_internal(void *server_handle, pent.pw_lockout_duration = 0; else pent.pw_lockout_duration = entry->pw_lockout_duration; - } else { - pent.pw_max_fail = 0; - pent.pw_failcnt_interval = 0; - pent.pw_lockout_duration = 0; } if ((ret = krb5_db_create_policy(handle->context, &pent))) @@ -209,13 +255,58 @@ kadm5_modify_policy(void *server_handle, return kadm5_modify_policy_internal(server_handle, entry, mask); } +/* Allocate and form a TL data list of a desired size. */ +static int +alloc_tl_data(krb5_int16 n_tl_data, krb5_tl_data **tldp) +{ + krb5_tl_data **tlp = tldp; + int i; + + for (i = 0; i < n_tl_data; i++) { + *tlp = calloc(1, sizeof(krb5_tl_data)); + if (*tlp == NULL) + return ENOMEM; /* caller cleans up */ + memset(*tlp, 0, sizeof(krb5_tl_data)); + tlp = &((*tlp)->tl_data_next); + } + + return 0; +} + +static kadm5_ret_t +copy_tl_data(krb5_int16 n_tl_data, krb5_tl_data *tl_data, + krb5_tl_data **out) +{ + kadm5_ret_t ret; + krb5_tl_data *tl, *tl_new; + + if ((ret = alloc_tl_data(n_tl_data, out))) + return ret; /* caller cleans up */ + + tl = tl_data; + tl_new = *out; + for (; tl; tl = tl->tl_data_next, tl_new = tl_new->tl_data_next) { + tl_new->tl_data_contents = malloc(tl->tl_data_length); + if (tl_new->tl_data_contents == NULL) + return ENOMEM; + memcpy(tl_new->tl_data_contents, tl->tl_data_contents, + tl->tl_data_length); + tl_new->tl_data_type = tl->tl_data_type; + tl_new->tl_data_length = tl->tl_data_length; + } + + return 0; +} + kadm5_ret_t kadm5_modify_policy_internal(void *server_handle, kadm5_policy_ent_t entry, long mask) { - kadm5_server_handle_t handle = server_handle; - osa_policy_ent_t p; - int ret; + kadm5_server_handle_t handle = server_handle; + krb5_tl_data *tl; + osa_policy_ent_t p; + int ret; + size_t len; CHECK_HANDLE(server_handle); @@ -225,6 +316,20 @@ kadm5_modify_policy_internal(void *server_handle, return KADM5_BAD_POLICY; if((mask & KADM5_POLICY)) return KADM5_BAD_MASK; + if ((mask & KADM5_POLICY_ALLOWED_KEYSALTS) && + entry->allowed_keysalts != NULL) { + ret = validate_allowed_keysalts(entry->allowed_keysalts); + if (ret) + return ret; + } + if ((mask & KADM5_POLICY_TL_DATA)) { + tl = entry->tl_data; + while (tl != NULL) { + if (tl->tl_data_type < 256) + return KADM5_BAD_TL_TYPE; + tl = tl->tl_data_next; + } + } ret = krb5_db_get_policy(handle->context, entry->policy, &p); if (ret == KRB5_KDB_NOENTRY) @@ -265,7 +370,7 @@ kadm5_modify_policy_internal(void *server_handle, } if ((mask & KADM5_REF_COUNT)) p->policy_refcnt = entry->policy_refcnt; - if (handle->api_version == KADM5_API_VERSION_3) { + if (handle->api_version >= KADM5_API_VERSION_3) { if ((mask & KADM5_PW_MAX_FAILURE)) p->pw_max_fail = entry->pw_max_fail; if ((mask & KADM5_PW_FAILURE_COUNT_INTERVAL)) @@ -273,7 +378,39 @@ kadm5_modify_policy_internal(void *server_handle, if ((mask & KADM5_PW_LOCKOUT_DURATION)) p->pw_lockout_duration = entry->pw_lockout_duration; } + if (handle->api_version >= KADM5_API_VERSION_4) { + if ((mask & KADM5_POLICY_ATTRIBUTES)) + p->attributes = entry->attributes; + if ((mask & KADM5_POLICY_MAX_LIFE)) + p->max_life = entry->max_life; + if ((mask & KADM5_POLICY_MAX_RLIFE)) + p->max_renewable_life = entry->max_renewable_life; + if ((mask & KADM5_POLICY_ALLOWED_KEYSALTS)) { + krb5_db_free(handle->context, p->allowed_keysalts); + p->allowed_keysalts = NULL; + if (entry->allowed_keysalts != NULL) { + len = strlen(entry->allowed_keysalts) + 1; + p->allowed_keysalts = krb5_db_alloc(handle->context, NULL, + len); + if (p->allowed_keysalts == NULL) { + ret = ENOMEM; + goto cleanup; + } + memcpy(p->allowed_keysalts, entry->allowed_keysalts, len); + } + } + if ((mask & KADM5_POLICY_TL_DATA)) { + for (tl = entry->tl_data; tl != NULL; tl = tl->tl_data_next) { + ret = krb5_db_update_tl_data(handle->context, &p->n_tl_data, + &p->tl_data, tl); + if (ret) + goto cleanup; + } + } + } ret = krb5_db_put_policy(handle->context, p); + +cleanup: krb5_db_free_policy(handle->context, p); return ret; } @@ -283,9 +420,11 @@ kadm5_get_policy(void *server_handle, kadm5_policy_t name, kadm5_policy_ent_t entry) { osa_policy_ent_t t; - int ret; + kadm5_ret_t ret; kadm5_server_handle_t handle = server_handle; + memset(entry, 0, sizeof(*entry)); + CHECK_HANDLE(server_handle); krb5_clear_error_message(handle->context); @@ -301,8 +440,8 @@ kadm5_get_policy(void *server_handle, kadm5_policy_t name, return ret; if ((entry->policy = strdup(t->name)) == NULL) { - krb5_db_free_policy(handle->context, t); - return ENOMEM; + ret = ENOMEM; + goto cleanup; } entry->pw_min_life = t->pw_min_life; entry->pw_max_life = t->pw_max_life; @@ -310,12 +449,33 @@ kadm5_get_policy(void *server_handle, kadm5_policy_t name, entry->pw_min_classes = t->pw_min_classes; entry->pw_history_num = t->pw_history_num; entry->policy_refcnt = t->policy_refcnt; - if (handle->api_version == KADM5_API_VERSION_3) { + if (handle->api_version >= KADM5_API_VERSION_3) { entry->pw_max_fail = t->pw_max_fail; entry->pw_failcnt_interval = t->pw_failcnt_interval; entry->pw_lockout_duration = t->pw_lockout_duration; } - krb5_db_free_policy(handle->context, t); + if (handle->api_version >= KADM5_API_VERSION_4) { + entry->attributes = t->attributes; + entry->max_life = t->max_life; + entry->max_renewable_life = t->max_renewable_life; + if (t->allowed_keysalts) { + entry->allowed_keysalts = strdup(t->allowed_keysalts); + if (!entry->allowed_keysalts) { + ret = ENOMEM; + goto cleanup; + } + } + ret = copy_tl_data(t->n_tl_data, t->tl_data, &entry->tl_data); + if (ret) + goto cleanup; + entry->n_tl_data = t->n_tl_data; + } + + ret = 0; - return KADM5_OK; +cleanup: + if (ret) + kadm5_free_policy_ent(handle, entry); + krb5_db_free_policy(handle->context, t); + return ret; } diff --git a/src/lib/kadm5/srv/svr_principal.c b/src/lib/kadm5/srv/svr_principal.c index f5ea005b2..f405f55ca 100644 --- a/src/lib/kadm5/srv/svr_principal.c +++ b/src/lib/kadm5/srv/svr_principal.c @@ -173,6 +173,138 @@ static void cleanup_key_data(context, count, data) krb5_db_free(context, data); } +/* Check whether a ks_tuple is present in an array of ks_tuples. */ +static krb5_boolean +ks_tuple_present(int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, + krb5_key_salt_tuple *looking_for) +{ + int i; + + for (i = 0; i < n_ks_tuple; i++) { + if (ks_tuple[i].ks_enctype == looking_for->ks_enctype && + ks_tuple[i].ks_salttype == looking_for->ks_salttype) + return TRUE; + } + return FALSE; +} + +/* + * Apply the -allowedkeysalts policy (see kadmin(1)'s addpol/modpol + * commands). We use the allowed key/salt tuple list as a default if + * no ks tuples as provided by the caller. We reject lists that include + * key/salts outside the policy. We re-order the requested ks tuples + * (which may be a subset of the policy) to reflect the policy order. + */ +static kadm5_ret_t +apply_keysalt_policy(kadm5_server_handle_t handle, const char *policy, + int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, + int *new_n_kstp, krb5_key_salt_tuple **new_kstp) +{ + kadm5_ret_t ret; + kadm5_policy_ent_rec polent; + int ak_n_ks_tuple = 0; + int new_n_ks_tuple = 0; + krb5_key_salt_tuple *ak_ks_tuple = NULL; + krb5_key_salt_tuple *new_ks_tuple = NULL; + krb5_key_salt_tuple *subset; + int i, m; + + if (new_n_kstp != NULL) { + *new_n_kstp = 0; + *new_kstp = NULL; + } + + memset(&polent, 0, sizeof(polent)); + if (policy != NULL && + (ret = kadm5_get_policy(handle->lhandle, (char *)policy, + &polent)) != KADM5_OK) { + if (ret == EINVAL) + ret = KADM5_BAD_POLICY; + if (ret) + goto cleanup; + } + + if (polent.allowed_keysalts == NULL && new_n_kstp != NULL) { + /* Requested keysalts allowed or default to supported_enctypes. */ + if (n_ks_tuple == 0) { + /* Default to supported_enctypes. */ + n_ks_tuple = handle->params.num_keysalts; + ks_tuple = handle->params.keysalts; + } + /* Dup the requested or defaulted keysalt tuples. */ + new_ks_tuple = malloc(n_ks_tuple * sizeof(*new_ks_tuple)); + if (new_ks_tuple == NULL) { + ret = ENOMEM; + goto cleanup; + } + memcpy(new_ks_tuple, ks_tuple, n_ks_tuple * sizeof(*new_ks_tuple)); + new_n_ks_tuple = n_ks_tuple; + ret = 0; + goto cleanup; + } + + ret = krb5_string_to_keysalts(polent.allowed_keysalts, + ", ", /* Tuple separators */ + ":.-", /* Key/salt separators */ + 0, /* No duplicates */ + &ak_ks_tuple, + &ak_n_ks_tuple); + /* + * Malformed policy? Shouldn't happen, but it's remotely possible + * someday, so we don't assert, just bail. + */ + if (ret) + goto cleanup; + + /* Check that the requested ks_tuples are within policy, if we have one. */ + for (i = 0; i < n_ks_tuple; i++) { + if (!ks_tuple_present(ak_n_ks_tuple, ak_ks_tuple, &ks_tuple[i])) { + ret = KADM5_BAD_KEYSALTS; + goto cleanup; + } + } + + /* Have policy but no ks_tuple input? Output the policy. */ + if (n_ks_tuple == 0) { + new_n_ks_tuple = ak_n_ks_tuple; + new_ks_tuple = ak_ks_tuple; + ak_ks_tuple = NULL; + goto cleanup; + } + + /* + * Now filter the policy ks tuples by the requested ones so as to + * preserve in the requested sub-set the relative ordering from the + * policy. We could optimize this (if (n_ks_tuple == ak_n_ks_tuple) + * then skip this), but we don't bother. + */ + subset = calloc(n_ks_tuple, sizeof(*subset)); + if (subset == NULL) { + ret = ENOMEM; + goto cleanup; + } + for (m = 0, i = 0; i < ak_n_ks_tuple && m < n_ks_tuple; i++) { + if (ks_tuple_present(n_ks_tuple, ks_tuple, &ak_ks_tuple[i])) + subset[m++] = ak_ks_tuple[i]; + } + new_ks_tuple = subset; + new_n_ks_tuple = m; + ret = 0; + +cleanup: + kadm5_free_policy_ent(handle->lhandle, &polent); + free(ak_ks_tuple); + + if (new_n_kstp != NULL) { + *new_n_kstp = new_n_ks_tuple; + *new_kstp = new_ks_tuple; + } else { + free(new_ks_tuple); + } + return ret; +} + + /* * Set *passptr to NULL if the request looks like the first part of a krb5 1.6 * addprinc -randkey operation. The krb5 1.6 dummy password for these requests @@ -224,6 +356,8 @@ kadm5_create_principal_3(void *server_handle, kadm5_server_handle_t handle = server_handle; krb5_keyblock *act_mkey; krb5_kvno act_kvno; + int new_n_ks_tuple = 0; + krb5_key_salt_tuple *new_ks_tuple = NULL; CHECK_HANDLE(server_handle); @@ -247,12 +381,6 @@ kadm5_create_principal_3(void *server_handle, if (entry == NULL) return EINVAL; - /* Use default keysalts if caller did not provide any. */ - if (n_ks_tuple == 0) { - ks_tuple = handle->params.keysalts; - n_ks_tuple = handle->params.num_keysalts; - } - /* * Check to see if the principal exists */ @@ -362,6 +490,16 @@ kadm5_create_principal_3(void *server_handle, } } + /* + * We need to have setup the TL data, so we have strings, so we can + * check enctype policy, which is why we check/initialize ks_tuple + * this late. + */ + ret = apply_keysalt_policy(handle, entry->policy, n_ks_tuple, ks_tuple, + &new_n_ks_tuple, &new_ks_tuple); + if (ret) + goto cleanup; + /* initialize the keys */ ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, &act_kvno, @@ -370,13 +508,14 @@ kadm5_create_principal_3(void *server_handle, goto cleanup; if (password) { - ret = krb5_dbe_cpw(handle->context, act_mkey, ks_tuple, n_ks_tuple, - password, (mask & KADM5_KVNO)?entry->kvno:1, + ret = krb5_dbe_cpw(handle->context, act_mkey, new_ks_tuple, + new_n_ks_tuple, password, + (mask & KADM5_KVNO)?entry->kvno:1, FALSE, kdb); } else { /* Null password means create with random key (new in 1.8). */ ret = krb5_dbe_crk(handle->context, &master_keyblock, - ks_tuple, n_ks_tuple, FALSE, kdb); + new_ks_tuple, new_n_ks_tuple, FALSE, kdb); } if (ret) goto cleanup; @@ -388,7 +527,7 @@ kadm5_create_principal_3(void *server_handle, ret = k5_kadm5_hook_create(handle->context, handle->hook_handles, KADM5_HOOK_STAGE_PRECOMMIT, entry, mask, - n_ks_tuple, ks_tuple, password); + new_n_ks_tuple, new_ks_tuple, password); if (ret) goto cleanup; @@ -441,9 +580,10 @@ kadm5_create_principal_3(void *server_handle, (void) k5_kadm5_hook_create(handle->context, handle->hook_handles, KADM5_HOOK_STAGE_POSTCOMMIT, entry, mask, - n_ks_tuple, ks_tuple, password); + new_n_ks_tuple, new_ks_tuple, password); cleanup: + free(new_ks_tuple); krb5_db_free_principal(handle->context, kdb); if (have_polent) (void) kadm5_free_policy_ent(handle->lhandle, &polent); @@ -1345,6 +1485,8 @@ kadm5_chpass_principal_3(void *server_handle, osa_pw_hist_ent hist; krb5_keyblock *act_mkey, *hist_keyblocks = NULL; krb5_kvno act_kvno, hist_kvno; + int new_n_ks_tuple = 0; + krb5_key_salt_tuple *new_ks_tuple = NULL; CHECK_HANDLE(server_handle); @@ -1359,15 +1501,14 @@ kadm5_chpass_principal_3(void *server_handle, principal, hist_princ)) == TRUE) return KADM5_PROTECT_PRINCIPAL; - /* Use default keysalts if caller did not provide any. */ - if (n_ks_tuple == 0) { - ks_tuple = handle->params.keysalts; - n_ks_tuple = handle->params.num_keysalts; - } - if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) return(ret); + ret = apply_keysalt_policy(handle, adb.policy, n_ks_tuple, ks_tuple, + &new_n_ks_tuple, &new_ks_tuple); + if (ret) + goto done; + if ((adb.aux_attributes & KADM5_POLICY)) { if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol))) goto done; @@ -1392,7 +1533,7 @@ kadm5_chpass_principal_3(void *server_handle, if (ret) goto done; - ret = krb5_dbe_cpw(handle->context, act_mkey, ks_tuple, n_ks_tuple, + ret = krb5_dbe_cpw(handle->context, act_mkey, new_ks_tuple, new_n_ks_tuple, password, 0 /* increment kvno */, keepold, kdb); if (ret) @@ -1504,7 +1645,7 @@ kadm5_chpass_principal_3(void *server_handle, ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles, KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold, - n_ks_tuple, ks_tuple, password); + new_n_ks_tuple, new_ks_tuple, password); if (ret) goto done; @@ -1513,9 +1654,10 @@ kadm5_chpass_principal_3(void *server_handle, (void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles, KADM5_HOOK_STAGE_POSTCOMMIT, principal, - keepold, n_ks_tuple, ks_tuple, password); + keepold, new_n_ks_tuple, new_ks_tuple, password); ret = KADM5_OK; done: + free(new_ks_tuple); if (!hist_added && hist.key_data) free_history_entry(handle->context, &hist); kdb_free_entry(handle, kdb, &adb); @@ -1554,39 +1696,41 @@ kadm5_randkey_principal_3(void *server_handle, int ret, last_pwd, have_pol = 0; kadm5_server_handle_t handle = server_handle; krb5_keyblock *act_mkey; + int new_n_ks_tuple = 0; + krb5_key_salt_tuple *new_ks_tuple = NULL; if (keyblocks) *keyblocks = NULL; CHECK_HANDLE(server_handle); - /* Use default keysalts if caller did not provide any. */ - if (n_ks_tuple == 0) { - ks_tuple = handle->params.keysalts; - n_ks_tuple = handle->params.num_keysalts; - } - krb5_clear_error_message(handle->context); if (principal == NULL) return EINVAL; + + if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) + return(ret); + + ret = apply_keysalt_policy(handle, adb.policy, n_ks_tuple, ks_tuple, + &new_n_ks_tuple, &new_ks_tuple); + if (ret) + goto done; + if (krb5_principal_compare(handle->context, principal, hist_princ)) { /* If changing the history entry, the new entry must have exactly one * key. */ if (keepold) return KADM5_PROTECT_PRINCIPAL; - n_ks_tuple = 1; + new_n_ks_tuple = 1; } - if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) - return(ret); - ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, NULL, &act_mkey); if (ret) goto done; - ret = krb5_dbe_crk(handle->context, act_mkey, ks_tuple, n_ks_tuple, + ret = krb5_dbe_crk(handle->context, act_mkey, new_ks_tuple, new_n_ks_tuple, keepold, kdb); if (ret) goto done; @@ -1650,7 +1794,7 @@ kadm5_randkey_principal_3(void *server_handle, ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles, KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold, - n_ks_tuple, ks_tuple, NULL); + new_n_ks_tuple, new_ks_tuple, NULL); if (ret) goto done; if ((ret = kdb_put_entry(handle, kdb, &adb))) @@ -1658,9 +1802,10 @@ kadm5_randkey_principal_3(void *server_handle, (void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles, KADM5_HOOK_STAGE_POSTCOMMIT, principal, - keepold, n_ks_tuple, ks_tuple, NULL); + keepold, new_n_ks_tuple, new_ks_tuple, NULL); ret = KADM5_OK; done: + free(new_ks_tuple); kdb_free_entry(handle, kdb, &adb); if (have_pol) kadm5_free_policy_ent(handle->lhandle, &pol); @@ -1838,6 +1983,24 @@ kadm5_setkey_principal(void *server_handle, keyblocks, n_keys); } +/* Make key/salt list from keys for kadm5_setkey_principal_3() */ +static kadm5_ret_t +make_ks_from_keys(krb5_context context, int n_keys, krb5_keyblock *keyblocks, + krb5_key_salt_tuple **ks_tuple) +{ + int i; + + *ks_tuple = calloc(n_keys, sizeof(**ks_tuple)); + if (ks_tuple == NULL) + return ENOMEM; + + for (i = 0; i < n_keys; i++) { + (*ks_tuple)[i].ks_enctype = keyblocks[i].enctype; + (*ks_tuple)[i].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL; + } + return 0; +} + kadm5_ret_t kadm5_setkey_principal_3(void *server_handle, krb5_principal principal, @@ -1862,6 +2025,7 @@ kadm5_setkey_principal_3(void *server_handle, krb5_key_data tmp_key_data; krb5_key_data *tptr; krb5_keyblock *act_mkey; + krb5_key_salt_tuple *ks_from_keys = NULL; CHECK_HANDLE(server_handle); @@ -1874,6 +2038,31 @@ kadm5_setkey_principal_3(void *server_handle, principal, hist_princ)) == TRUE)) return KADM5_PROTECT_PRINCIPAL; + if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) + return(ret); + + if (!n_ks_tuple) { + /* Apply policy to the key/salt types implied by the given keys */ + ret = make_ks_from_keys(handle->context, n_keys, keyblocks, + &ks_from_keys); + if (ret) + goto done; + ret = apply_keysalt_policy(handle, adb.policy, n_keys, ks_from_keys, + NULL, NULL); + free(ks_from_keys); + } else { + /* + * Apply policy to the given ks_tuples. Note that further below + * we enforce keyblocks[i].enctype == ks_tuple[i].ks_enctype for + * all i from 0 to n_keys, and that n_ks_tuple == n_keys if ks + * tuples are given. + */ + ret = apply_keysalt_policy(handle, adb.policy, n_ks_tuple, ks_tuple, + NULL, NULL); + } + if (ret) + goto done; + for (i = 0; i < n_keys; i++) { for (j = i+1; j < n_keys; j++) { if ((ret = krb5_c_enctype_compare(handle->context, @@ -1894,9 +2083,6 @@ kadm5_setkey_principal_3(void *server_handle, if (n_ks_tuple && n_ks_tuple != n_keys) return KADM5_SETKEY3_ETYPE_MISMATCH; - if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) - return(ret); - for (kvno = 0, i=0; i<kdb->n_key_data; i++) if (kdb->key_data[i].key_data_kvno > kvno) kvno = kdb->key_data[i].key_data_kvno; diff --git a/src/lib/kadm5/unit-test/destroy-test.c b/src/lib/kadm5/unit-test/destroy-test.c index ccca24928..738cfeb86 100644 --- a/src/lib/kadm5/unit-test/destroy-test.c +++ b/src/lib/kadm5/unit-test/destroy-test.c @@ -28,7 +28,7 @@ int main() } for(x = 0; x < TEST_NUM; x++) { ret = kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, 0, - KADM5_STRUCT_VERSION, KADM5_API_VERSION_3, NULL, + KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, NULL, &server_handle); if(ret != KADM5_OK) { com_err("test", ret, "init"); diff --git a/src/lib/kadm5/unit-test/handle-test.c b/src/lib/kadm5/unit-test/handle-test.c index a77bc1195..29bd2c9a1 100644 --- a/src/lib/kadm5/unit-test/handle-test.c +++ b/src/lib/kadm5/unit-test/handle-test.c @@ -31,7 +31,7 @@ int main(int argc, char *argv[]) kadm5_init_krb5_context(&context); ret = kadm5_init(context, "admin/none", "admin", KADM5_ADMIN_SERVICE, NULL, - KADM5_STRUCT_VERSION, KADM5_API_VERSION_3, NULL, + KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, NULL, &server_handle); if(ret != KADM5_OK) { com_err("test", ret, "init"); diff --git a/src/lib/kadm5/unit-test/init-test.c b/src/lib/kadm5/unit-test/init-test.c index 354b812b6..880400c9d 100644 --- a/src/lib/kadm5/unit-test/init-test.c +++ b/src/lib/kadm5/unit-test/init-test.c @@ -21,7 +21,7 @@ int main() exit(1); } ret = kadm5_init(context, "admin", "admin", NULL, ¶ms, - KADM5_STRUCT_VERSION, KADM5_API_VERSION_3, NULL, + KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, NULL, &server_handle); if (ret == KADM5_RPC_ERROR) { krb5_free_context(context); diff --git a/src/lib/kadm5/unit-test/iter-test.c b/src/lib/kadm5/unit-test/iter-test.c index bc7cfdcfa..cd85ebe4d 100644 --- a/src/lib/kadm5/unit-test/iter-test.c +++ b/src/lib/kadm5/unit-test/iter-test.c @@ -23,7 +23,7 @@ int main(int argc, char **argv) exit(1); } ret = kadm5_init("admin", "admin", KADM5_ADMIN_SERVICE, 0, - KADM5_STRUCT_VERSION, KADM5_API_VERSION_3, NULL, + KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, NULL, &server_handle); if (ret != KADM5_OK) { com_err("iter-test", ret, "while initializing"); diff --git a/src/lib/kadm5/unit-test/randkey-test.c b/src/lib/kadm5/unit-test/randkey-test.c index 7cf4ee8ac..dbef88ac8 100644 --- a/src/lib/kadm5/unit-test/randkey-test.c +++ b/src/lib/kadm5/unit-test/randkey-test.c @@ -24,7 +24,7 @@ int main() krb5_parse_name(context, "testuser", &tprinc); ret = kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL, - KADM5_STRUCT_VERSION, KADM5_API_VERSION_3, NULL, + KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, NULL, &server_handle); if(ret != KADM5_OK) { com_err("test", ret, "init"); diff --git a/src/lib/kadm5/unit-test/setkey-test.c b/src/lib/kadm5/unit-test/setkey-test.c index 53056e434..c1b9c5d1f 100644 --- a/src/lib/kadm5/unit-test/setkey-test.c +++ b/src/lib/kadm5/unit-test/setkey-test.c @@ -120,7 +120,7 @@ main(int argc, char **argv) } ret = kadm5_init(context, authprinc, NULL, KADM5_ADMIN_SERVICE, NULL, - KADM5_STRUCT_VERSION, KADM5_API_VERSION_3, NULL, + KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, NULL, &handle); if (ret) { com_err(whoami, ret, "while initializing connection"); diff --git a/src/lib/kdb/kdb5.c b/src/lib/kdb/kdb5.c index a3c2a5f9c..3cf116b76 100644 --- a/src/lib/kdb/kdb5.c +++ b/src/lib/kdb/kdb5.c @@ -2185,8 +2185,8 @@ krb5_dbe_delete_tl_data(krb5_context context, krb5_db_entry *entry, } krb5_error_code -krb5_dbe_update_tl_data(krb5_context context, krb5_db_entry *entry, - krb5_tl_data *new_tl_data) +krb5_db_update_tl_data(krb5_context context, krb5_int16 *n_tl_datap, + krb5_tl_data **tl_datap, krb5_tl_data *new_tl_data) { krb5_tl_data *tl_data = NULL; krb5_octet *tmp; @@ -2206,7 +2206,7 @@ krb5_dbe_update_tl_data(krb5_context context, krb5_db_entry *entry, */ if (new_tl_data->tl_data_type != KRB5_TL_DB_ARGS) { /* db_args can be multiple */ - for (tl_data = entry->tl_data; tl_data; + for (tl_data = *tl_datap; tl_data; tl_data = tl_data->tl_data_next) if (tl_data->tl_data_type == new_tl_data->tl_data_type) break; @@ -2221,9 +2221,9 @@ krb5_dbe_update_tl_data(krb5_context context, krb5_db_entry *entry, return (ENOMEM); } memset(tl_data, 0, sizeof(krb5_tl_data)); - tl_data->tl_data_next = entry->tl_data; - entry->tl_data = tl_data; - entry->n_tl_data++; + tl_data->tl_data_next = *tl_datap; + *tl_datap = tl_data; + (*n_tl_datap)++; } /* fill in the record */ @@ -2240,6 +2240,14 @@ krb5_dbe_update_tl_data(krb5_context context, krb5_db_entry *entry, } krb5_error_code +krb5_dbe_update_tl_data(krb5_context context, krb5_db_entry *entry, + krb5_tl_data *new_tl_data) +{ + return krb5_db_update_tl_data(context, &entry->n_tl_data, &entry->tl_data, + new_tl_data); +} + +krb5_error_code krb5_dbe_compute_salt(krb5_context context, const krb5_key_data *key, krb5_const_principal princ, krb5_int16 *salttype_out, krb5_data **salt_out) diff --git a/src/lib/kdb/libkdb5.exports b/src/lib/kdb/libkdb5.exports index 0e58262b2..9aa8d1a4a 100644 --- a/src/lib/kdb/libkdb5.exports +++ b/src/lib/kdb/libkdb5.exports @@ -65,6 +65,7 @@ krb5_dbe_update_mkey_aux krb5_dbe_update_mkvno krb5_dbe_update_mod_princ_data krb5_dbe_update_tl_data +krb5_db_update_tl_data krb5_dbe_def_encrypt_key_data krb5_dbe_def_decrypt_key_data krb5_dbe_decrypt_key_data diff --git a/src/plugins/kdb/db2/pol_xdr.c b/src/plugins/kdb/db2/pol_xdr.c index 315d0d1c5..e8576337c 100644 --- a/src/plugins/kdb/db2/pol_xdr.c +++ b/src/plugins/kdb/db2/pol_xdr.c @@ -2,68 +2,29 @@ #include <krb5.h> #include <gssrpc/rpc.h> #include <kdb.h> +#include <kadm5/admin_xdr.h> #include "policy_db.h" #ifdef HAVE_MEMORY_H #include <memory.h> #endif #include <string.h> -static -bool_t xdr_nullstring(XDR *xdrs, char **objp) -{ - u_int size; - - if (xdrs->x_op == XDR_ENCODE) { - if (*objp == NULL) - size = 0; - else - size = strlen(*objp) + 1; - } - if (! xdr_u_int(xdrs, &size)) { - return FALSE; - } - switch (xdrs->x_op) { - case XDR_DECODE: - if (size == 0) { - *objp = NULL; - return TRUE; - } else if (*objp == NULL) { - *objp = (char *) mem_alloc(size); - if (*objp == NULL) { - errno = ENOMEM; - return FALSE; - } - } - return (xdr_opaque(xdrs, *objp, size)); - - case XDR_ENCODE: - if (size != 0) - return (xdr_opaque(xdrs, *objp, size)); - return TRUE; - - case XDR_FREE: - if (*objp != NULL) - mem_free(*objp, size); - *objp = NULL; - return TRUE; - } - - return FALSE; -} - static int osa_policy_min_vers(osa_policy_ent_t objp) { - int vers; + if (objp->attributes || + objp->max_life || + objp->max_renewable_life || + objp->allowed_keysalts || + objp->n_tl_data) + return OSA_ADB_POLICY_VERSION_3; if (objp->pw_max_fail || objp->pw_failcnt_interval || objp->pw_lockout_duration) - vers = OSA_ADB_POLICY_VERSION_2; - else - vers = OSA_ADB_POLICY_VERSION_1; + return OSA_ADB_POLICY_VERSION_2; - return vers; + return OSA_ADB_POLICY_VERSION_1; } bool_t @@ -81,7 +42,8 @@ xdr_osa_policy_ent_rec(XDR *xdrs, osa_policy_ent_t objp) if (!xdr_int(xdrs, &objp->version)) return FALSE; if (objp->version != OSA_ADB_POLICY_VERSION_1 && - objp->version != OSA_ADB_POLICY_VERSION_2) + objp->version != OSA_ADB_POLICY_VERSION_2 && + objp->version != OSA_ADB_POLICY_VERSION_3) return FALSE; break; } @@ -108,5 +70,20 @@ xdr_osa_policy_ent_rec(XDR *xdrs, osa_policy_ent_t objp) if (!xdr_u_int32(xdrs, &objp->pw_lockout_duration)) return (FALSE); } + if (objp->version > OSA_ADB_POLICY_VERSION_2) { + if (!xdr_u_int32(xdrs, &objp->attributes)) + return (FALSE); + if (!xdr_u_int32(xdrs, &objp->max_life)) + return (FALSE); + if (!xdr_u_int32(xdrs, &objp->max_renewable_life)) + return (FALSE); + if (!xdr_nullstring(xdrs, &objp->allowed_keysalts)) + return (FALSE); + if (!xdr_short(xdrs, &objp->n_tl_data)) + return (FALSE); + if (!xdr_nulltype(xdrs, (void **) &objp->tl_data, + xdr_krb5_tl_data)) + return FALSE; + } return (TRUE); } diff --git a/src/plugins/kdb/db2/policy_db.h b/src/plugins/kdb/db2/policy_db.h index 6c920bc44..07026e35a 100644 --- a/src/plugins/kdb/db2/policy_db.h +++ b/src/plugins/kdb/db2/policy_db.h @@ -41,6 +41,7 @@ typedef long osa_adb_ret_t; #define OSA_ADB_POLICY_VERSION_MASK 0x12345D00 #define OSA_ADB_POLICY_VERSION_1 0x12345D01 #define OSA_ADB_POLICY_VERSION_2 0x12345D02 +#define OSA_ADB_POLICY_VERSION_3 0x12345D03 diff --git a/src/slave/kpropd.c b/src/slave/kpropd.c index aa6e979ed..309717dd9 100644 --- a/src/slave/kpropd.c +++ b/src/slave/kpropd.c @@ -713,7 +713,7 @@ reinit: master_svc_princstr, ¶ms, KADM5_STRUCT_VERSION, - KADM5_API_VERSION_3, + KADM5_API_VERSION_4, db_args, &server_handle); diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in index 210bd8d7c..39a047ec1 100644 --- a/src/tests/Makefile.in +++ b/src/tests/Makefile.in @@ -69,6 +69,7 @@ check-pytests:: hist $(RUNPYTEST) $(srcdir)/t_lockout.py $(PYTESTFLAGS) $(RUNPYTEST) $(srcdir)/t_kadm5_hook.py $(PYTESTFLAGS) $(RUNPYTEST) $(srcdir)/t_keyrollover.py $(PYTESTFLAGS) + $(RUNPYTEST) $(srcdir)/t_allowed_keysalts.py $(PYTESTFLAGS) $(RUNPYTEST) $(srcdir)/t_renew.py $(PYTESTFLAGS) $(RUNPYTEST) $(srcdir)/t_renprinc.py $(PYTESTFLAGS) $(RUNPYTEST) $(srcdir)/t_ccache.py $(PYTESTFLAGS) diff --git a/src/tests/hist.c b/src/tests/hist.c index c0b2b978c..3d9e29e36 100644 --- a/src/tests/hist.c +++ b/src/tests/hist.c @@ -72,7 +72,7 @@ main(int argc, char **argv) params.mask |= KADM5_CONFIG_REALM; params.realm = realm; check(kadm5_init(ctx, "user", "", "", ¶ms, KADM5_STRUCT_VERSION, - KADM5_API_VERSION_3, NULL, &handle)); + KADM5_API_VERSION_4, NULL, &handle)); if (strcmp(argv[1], "make") == 0) { memset(&kent, 0, sizeof(kent)); kent.principal = hprinc; diff --git a/src/tests/t_allowed_keysalts.py b/src/tests/t_allowed_keysalts.py new file mode 100644 index 000000000..8c763358a --- /dev/null +++ b/src/tests/t_allowed_keysalts.py @@ -0,0 +1,93 @@ +#!/usr/bin/python +from k5test import * +import re + +krb5_conf1 = {'all': {'libdefaults': { + 'supported_enctypes': 'aes256-cts'}}} + +realm = K5Realm(krb5_conf=krb5_conf1, create_host=False, get_creds=False) + +# Add policy. +realm.run_kadminl('addpol -allowedkeysalts aes256-cts:normal ak') +realm.run_kadminl('addprinc -randkey -e aes256-cts:normal server') + +# Test with one-enctype allowed_keysalts. +realm.run_kadminl('modprinc -policy ak server') +realm.run_kadminl('getprinc server') +output = realm.run_kadminl('cpw -randkey -e aes128-cts:normal server') +if not 'Invalid key/salt tuples' in output: + fail('allowed_keysalts policy not applied properly') +realm.run_kadminl('getprinc server') +output = realm.run_kadminl('cpw -randkey -e aes256-cts:normal server') +if 'Invalid key/salt tuples' in output: + fail('allowed_keysalts policy not applied properly') +realm.run_kadminl('getprinc server') + +# Now test a multi-enctype allowed_keysalts. Test that subsets are allowed, +# the the complete set is allowed, that order doesn't matter, and that +# enctypes outside the set are not allowed. + +# Test modpol. +realm.run_kadminl('modpol -allowedkeysalts ' + 'aes256-cts:normal,rc4-hmac:normal ak') +output = realm.run_kadminl('getpol ak') +if not 'Allowed key/salt types: aes256-cts:normal,rc4-hmac:normal' in output: + fail('getpol does not implement allowedkeysalts?') + +# Test one subset. +output = realm.run_kadminl('cpw -randkey -e rc4-hmac:normal server') +if 'Invalid key/salt tuples' in output: + fail('allowed_keysalts policy not applied properly') +realm.run_kadminl('getprinc server') + +# Test another subset. +output = realm.run_kadminl('cpw -randkey -e aes256-cts:normal server') +if 'Invalid key/salt tuples' in output: + fail('allowed_keysalts policy not applied properly') +realm.run_kadminl('getprinc server') +output = realm.run_kadminl('cpw -randkey -e ' + 'rc4-hmac:normal,aes256-cts:normal server') +if 'Invalid key/salt tuples' in output: + fail('allowed_keysalts policy not applied properly') +realm.run_kadminl('getprinc server') + +# Test full set. +output = realm.run_kadminl('cpw -randkey -e aes256-cts:normal,rc4-hmac:normal ' + 'server') +if 'Invalid key/salt tuples' in output: + fail('allowed_keysalts policy not applied properly') +realm.run_kadminl('getprinc server') +output = realm.run_kadminl('cpw -randkey -e rc4-hmac:normal,aes128-cts:normal ' + 'server') +if not 'Invalid key/salt tuples' in output: + fail('allowed_keysalts policy not applied properly') +realm.run_kadminl('getprinc server') +output = realm.run_kadminl('getprinc -terse server') +if not '2\t1\t6\t18\t0\t1\t6\t23\t0' in output: + fail('allowed_keysalts policy did not preserve order') + +# Test full set in opposite order. +output = realm.run_kadminl('cpw -randkey -e rc4-hmac:normal,aes256-cts:normal,' + 'aes128-cts:normal server') +if not 'Invalid key/salt tuples' in output: + fail('allowed_keysalts policy not applied properly') + +# Check that the order we got is the one from the policy. +realm.run_kadminl('getprinc server') +output = realm.run_kadminl('getprinc -terse server') +if not '2\t1\t6\t18\t0\t1\t6\t23\t0' in output: + fail('allowed_keysalts policy did not preserve order') + +# Test reset of allowedkeysalts. +realm.run_kadminl('modpol -allowedkeysalts - ak') +output = realm.run_kadminl('getpol ak') +if 'Allowed key/salt types' in output: + fail('failed to clear allowedkeysalts') +output = realm.run_kadminl('cpw -randkey -e aes128-cts:normal server') +if 'Invalid key/salt tuples' in output: + fail('key change rejected that should have been permitted') +realm.run_kadminl('getprinc server') + +realm.stop() + +success('allowed_keysalts') diff --git a/src/tests/t_general.py b/src/tests/t_general.py index 2b04b8eae..77246d52f 100755 --- a/src/tests/t_general.py +++ b/src/tests/t_general.py @@ -28,10 +28,26 @@ realm = K5Realm(create_host=False) realm.run_kadminl('addpol fred') dumpfile = os.path.join(realm.testdir, 'dump') realm.run_as_master([kdb5_util, 'dump', dumpfile]) +f = open('testdir/dump', 'a') +f.write('policy barney 0 0 1 1 1 0 ' + '0 0 0 0 0 0 - 1 ' + '2 28 ' + 'fd100f5064625f6372656174696f6e404b5242544553542e434f4d00') +f.close() +realm.run_as_master([kdb5_util, 'load', dumpfile]) +output = realm.run_kadminl('getpols') +if 'fred\n' not in output: + fail('Policy not preserved across dump/load.') +if 'barney\n' not in output: + fail('Policy not loaded.') + +realm.run_as_master([kdb5_util, 'dump', dumpfile]) realm.run_as_master([kdb5_util, 'load', dumpfile]) output = realm.run_kadminl('getpols') if 'fred\n' not in output: fail('Policy not preserved across dump/load.') +if 'barney\n' not in output: + fail('Policy not preserved across dump/load.') # Spot-check KRB5_TRACE output tracefile = os.path.join(realm.testdir, 'trace') |