Add secinfo processing. From: Fred Isaman Signed-off-by: Fred Isaman Add secinfo processing. --- support/include/nfslib.h | 6 ++ support/nfs/exports.c | 145 ++++++++++++++++++++++++++++++++++++++++++++- utils/exportfs/exportfs.c | 3 + utils/mountd/cache.c | 14 ++++ 4 files changed, 164 insertions(+), 4 deletions(-) Try to use kernel function to determine supported Kerberos enctypes. From: Kevin Coffman Signed-off-by: Kevin Coffman This patch replaces a hard-coded list with a function to obtain the Kerberos encryption types that the kernel's rpcsec_gss code can support. Defaults to old behavior if kernel does not supply information. --- utils/gssd/gssd.c | 2 utils/gssd/krb5_util.c | 228 +++++++++++++++++++++++++++++++++++++----------- utils/gssd/krb5_util.h | 2 3 files changed, 182 insertions(+), 50 deletions(-) Add EXPERIMENTAL support for non-DES encryption types. From: Kevin Coffman Signed-off-by: Kevin Coffman Adds EXPERIMENTAL support for non-DES encryption types. Sends a new format of context information to the kernel. (Requires kernel support to do anything useful.) --- utils/gssd/context.h | 6 + utils/gssd/context_lucid.c | 391 ++++++++++++++++++++++++++++++++++++++++++-- utils/gssd/context_mit.c | 254 +++++++++++++++++++++++++---- 3 files changed, 603 insertions(+), 48 deletions(-) diff --git a/support/include/nfslib.h b/support/include/nfslib.h index c085029..6d7743d 100644 --- a/support/include/nfslib.h +++ b/support/include/nfslib.h @@ -57,6 +57,9 @@ enum cle_maptypes { #define _PATH_PROC_EXPORTS_ALT "/proc/fs/nfsd/exports" #endif +#define SECFLAVOR_COUNT 7 +extern char *secflavor_name[SECFLAVOR_COUNT]; + /* * Data related to a single exports entry as returned by getexportent. * FIXME: export options should probably be parsed at a later time to @@ -83,6 +86,8 @@ struct exportent { int e_fslocmethod; char * e_fslocdata; char * e_uuid; + int e_secinfo_order[SECFLAVOR_COUNT+1]; + int e_secinfo_flags[SECFLAVOR_COUNT]; }; struct rmtabent { @@ -96,6 +101,7 @@ struct rmtabent { */ void setexportent(char *fname, char *type); struct exportent * getexportent(int,int); +void secinfo_show(FILE *fp, struct exportent *ep); void putexportent(struct exportent *xep); void endexportent(void); struct exportent * mkexportent(char *hname, char *path, char *opts); diff --git a/support/nfs/exports.c b/support/nfs/exports.c index 294e1c9..af9ab2f 100644 --- a/support/nfs/exports.c +++ b/support/nfs/exports.c @@ -51,6 +51,10 @@ static int parsenum(char **cpp); static int parsenum(char **cpp); static void freesquash(void); static void syntaxerr(char *msg); +static unsigned int parse_flavors(char *str, struct exportent *ep); +static int secinfo_default(struct exportent *ep); +static void setflags(int mask, unsigned int *ap, struct exportent *ep); +static void clearflags(int mask, unsigned int *ap, struct exportent *ep); void setexportent(char *fname, char *type) @@ -102,6 +106,7 @@ getexportent(int fromkernel, int fromexp def_ee.e_mountpoint = NULL; def_ee.e_fslocmethod = FSLOC_NONE; def_ee.e_fslocdata = NULL; + def_ee.e_secinfo_order[0] = -1; def_ee.e_nsquids = 0; def_ee.e_nsqgids = 0; @@ -182,6 +187,19 @@ getexportent(int fromkernel, int fromexp } void +secinfo_show(FILE *fp, struct exportent *ep) +{ + int *p1, *p2; + for (p1=ep->e_secinfo_order; *p1>=0; p1=p2) { + fprintf(fp, ",sec=%s", secflavor_name[*p1]); + for (p2=p1+1; (*p2>=0) && (ep->e_secinfo_flags[*p1]==ep->e_secinfo_flags[*p2]); p2++) { + fprintf(fp, ":%s", secflavor_name[*p2]); + } + fprintf(fp, ",%s", (ep->e_secinfo_flags[*p1] & NFSEXP_READONLY)? "ro" : "rw"); + } +} + +void putexportent(struct exportent *ep) { FILE *fp; @@ -199,7 +217,6 @@ putexportent(struct exportent *ep) fprintf(fp, "%c", esc[i]); fprintf(fp, "\t%s(", ep->e_hostname); - fprintf(fp, "%s,", (ep->e_flags & NFSEXP_READONLY)? "ro" : "rw"); fprintf(fp, "%ssync,", (ep->e_flags & NFSEXP_ASYNC)? "a" : ""); fprintf(fp, "%swdelay,", (ep->e_flags & NFSEXP_GATHERED_WRITES)? "" : "no_"); @@ -276,7 +293,9 @@ #endif else fprintf(fp, "%d,", id[i]); } - fprintf(fp, "anonuid=%d,anongid=%d)\n", ep->e_anonuid, ep->e_anongid); + fprintf(fp, "anonuid=%d,anongid=%d", ep->e_anonuid, ep->e_anongid); + secinfo_show(fp, ep); + fprintf(fp, ")\n"); } void @@ -325,6 +344,7 @@ mkexportent(char *hname, char *path, cha ee.e_mountpoint = NULL; ee.e_fslocmethod = FSLOC_NONE; ee.e_fslocdata = NULL; + ee.e_secinfo_order[0] = -1; ee.e_nsquids = 0; ee.e_nsqgids = 0; ee.e_uuid = NULL; @@ -376,6 +396,9 @@ parseopts(char *cp, struct exportent *ep int had_subtree_opt = 0; char *flname = efname?efname:"command line"; int flline = efp?efp->x_line:0; + int *p; + unsigned int active = 0; + int secmask = NFSEXP_READONLY; /* options that can vary per flavor */ squids = ep->e_squids; nsquids = ep->e_nsquids; sqgids = ep->e_sqgids; nsqgids = ep->e_nsqgids; @@ -398,9 +421,9 @@ parseopts(char *cp, struct exportent *ep /* process keyword */ if (strcmp(opt, "ro") == 0) - ep->e_flags |= NFSEXP_READONLY; + setflags(NFSEXP_READONLY, &active, ep); else if (strcmp(opt, "rw") == 0) - ep->e_flags &= ~NFSEXP_READONLY; + clearflags(NFSEXP_READONLY, &active, ep); else if (!strcmp(opt, "secure")) ep->e_flags &= ~NFSEXP_INSECURE_PORT; else if (!strcmp(opt, "insecure")) @@ -522,6 +545,10 @@ #endif } else if (strncmp(opt, "replicas=", 9) == 0) { ep->e_fslocmethod = FSLOC_REPLICA; ep->e_fslocdata = strdup(opt+9); + } else if (strncmp(opt, "sec=", 4) == 0) { + active = parse_flavors(opt+4, ep); + if (!active) + goto bad_option; } else { xlog(L_ERROR, "%s:%d: unknown keyword \"%s\"\n", flname, flline, opt); @@ -533,6 +560,12 @@ #endif cp++; } + if (!active) + active = secinfo_default(ep); + for (p=ep->e_secinfo_order; *p>=0; p++) + ep->e_secinfo_flags[*p] |= (ep->e_flags & ~secmask); + /* If did not use sec= option, ensure e_flags is backward compatible */ + ep->e_flags = ep->e_secinfo_flags[ep->e_secinfo_order[0]]; ep->e_squids = squids; ep->e_sqgids = sqgids; ep->e_nsquids = nsquids; @@ -663,3 +696,107 @@ syntaxerr(char *msg) efname, efp?efp->x_line:0, msg); } +char *secflavor_name[SECFLAVOR_COUNT] = { "sys", + "krb5", + "krb5i", + "krb5p", + "spkm3", + "spkm3i", + "spkm3p" +}; + +static void +secinfo_addflavor(int bit, struct exportent *ep) +{ + int *p; + for (p=ep->e_secinfo_order; *p>=0; p++) { + if (*p == bit) + return; + } + *p++ = bit; + *p = -1; + ep->e_secinfo_flags[bit] = 0; +} + +static int +secinfo_nameindex(char *name) +{ + int i; + for (i=0; ie_hostname. */ +static int +secinfo_default(struct exportent *ep) +{ + int i=-1; + if (strncmp(ep->e_hostname, "gss/", 4) == 0) { + i = secinfo_nameindex(ep->e_hostname + 4); + if (i < 0) + xlog(L_WARNING, "unknown flavor %s\n", ep->e_hostname); + } + /* Default to auth_sys */ + if (i < 0) + i = secinfo_nameindex("sys"); + secinfo_addflavor(i, ep); + return 1<e_secinfo_flags[flavor] |= mask; + flavor++; + active >>= 1; + } +} + +/* Clears the bits in @mask for the appropriate security flavor flags. */ +static void +clearflags(int mask, unsigned int *ap, struct exportent *ep) +{ + int active, flavor=0; + if (!*ap) + *ap = secinfo_default(ep); + active = *ap; + while (active) { + if (active & 1) + ep->e_secinfo_flags[flavor] &= ~mask; + flavor++; + active >>= 1; + } +} diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c index 22e13a3..30732b8 100644 --- a/utils/exportfs/exportfs.c +++ b/utils/exportfs/exportfs.c @@ -376,10 +376,12 @@ dump(int verbose) continue; } c = '('; + /* if (ep->e_flags & NFSEXP_READONLY) c = dumpopt(c, "ro"); else c = dumpopt(c, "rw"); + */ if (ep->e_flags & NFSEXP_ASYNC) c = dumpopt(c, "async"); if (ep->e_flags & NFSEXP_GATHERED_WRITES) @@ -433,6 +435,7 @@ #ifdef DEBUG break; #endif } + secinfo_show(stdout, ep); printf("%c\n", (c != '(')? ')' : ' '); } } diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c index 5f0d12a..78a8f75 100644 --- a/utils/mountd/cache.c +++ b/utils/mountd/cache.c @@ -445,6 +445,19 @@ static void write_fsloc(FILE *f, struct release_replicas(servers); } +static void write_secinfo(FILE *f, struct exportent *ep) +{ + int *p; + qword_print(f, "secinfo"); + for (p=ep->e_secinfo_order; *p>=0; p++) + ; /* Do nothing */ + qword_printint(f, p - ep->e_secinfo_order); + for (p=ep->e_secinfo_order; *p>=0; p++) { + qword_print(f, secflavor_name[*p]); + qword_printint(f, ep->e_secinfo_flags[*p]); + } +} + static int dump_to_cache(FILE *f, char *domain, char *path, struct exportent *exp) { qword_print(f, domain); @@ -466,6 +479,7 @@ static int dump_to_cache(FILE *f, char * qword_printhex(f, exp->e_uuid, 16); } #endif + write_secinfo(f, exp); } return qword_eol(f); } diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c index 08a6c10..44afdd0 100644 --- a/utils/gssd/gssd.c +++ b/utils/gssd/gssd.c @@ -162,6 +162,8 @@ #endif /* Process keytab file and get machine credentials */ if (root_uses_machine_creds) gssd_refresh_krb5_machine_creds(); + /* Determine Kerberos information from the kernel */ + gssd_obtain_kernel_krb5_info(); gssd_run(); printerr(0, "gssd_run returned!\n"); diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c index 096f6cf..96e91ae 100644 --- a/utils/gssd/krb5_util.c +++ b/utils/gssd/krb5_util.c @@ -97,6 +97,7 @@ #endif #include "config.h" #include #include +#include #include #include #include @@ -105,6 +106,7 @@ #include #include #include #include +#include #include #include #include @@ -123,6 +125,10 @@ #include "krb5_util.h" /* Global list of principals/cache file names for machine credentials */ struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL; +/* Encryption types supported by the kernel rpcsec_gss code */ +int num_krb5_enctypes = 0; +krb5_enctype *krb5_enctypes = NULL; + /*==========================*/ /*=== Internal routines ===*/ /*==========================*/ @@ -256,56 +262,6 @@ gssd_find_existing_krb5_ccache(uid_t uid } -#ifdef HAVE_SET_ALLOWABLE_ENCTYPES -/* - * this routine obtains a credentials handle via gss_acquire_cred() - * then calls gss_krb5_set_allowable_enctypes() to limit the encryption - * types negotiated. - * - * XXX Should call some function to determine the enctypes supported - * by the kernel. (Only need to do that once!) - * - * Returns: - * 0 => all went well - * -1 => there was an error - */ - -int -limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid) -{ - u_int maj_stat, min_stat; - gss_cred_id_t credh; - gss_OID_set_desc desired_mechs; - krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC }; - int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]); - - /* We only care about getting a krb5 cred */ - desired_mechs.count = 1; - desired_mechs.elements = &krb5oid; - - maj_stat = gss_acquire_cred(&min_stat, NULL, 0, - &desired_mechs, GSS_C_INITIATE, - &credh, NULL, NULL); - - if (maj_stat != GSS_S_COMPLETE) { - pgsserr("gss_acquire_cred", - maj_stat, min_stat, &krb5oid); - return -1; - } - - maj_stat = gss_set_allowable_enctypes(&min_stat, credh, &krb5oid, - num_enctypes, &enctypes); - if (maj_stat != GSS_S_COMPLETE) { - pgsserr("gss_set_allowable_enctypes", - maj_stat, min_stat, &krb5oid); - return -1; - } - sec->cred = credh; - - return 0; -} -#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */ - /* * Obtain credentials via a key in the keytab given * a keytab handle and a gssd_k5_kt_princ structure. @@ -609,6 +565,56 @@ #else #endif } +/* + * Parse the supported encryption type information + */ +static int +parse_enctypes(char *enctypes) +{ + int n = 0; + char *curr, *comma; + int i; + + /* Just in case this ever gets called more than once */ + if (krb5_enctypes != NULL) { + free(krb5_enctypes); + krb5_enctypes = NULL; + num_krb5_enctypes = 0; + } + + /* count the number of commas */ + for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) { + comma = strchr(curr, ','); + if (comma != NULL) + n++; + else + break; + } + /* If no more commas and we're not at the end, there's one more value */ + if (*curr != '\0') + n++; + + /* Empty string, return an error */ + if (n == 0) + return ENOENT; + + /* Allocate space for enctypes array */ + if ((krb5_enctypes = (int *) calloc(n, sizeof(int))) == NULL) { + return ENOMEM; + } + + /* Now parse each value into the array */ + for (curr = enctypes, i = 0; curr && *curr != '\0'; curr = ++comma) { + krb5_enctypes[i++] = atoi(curr); + comma = strchr(curr, ','); + if (comma == NULL) + break; + } + + num_krb5_enctypes = n; + return 0; +} + /*==========================*/ /*=== External routines ===*/ /*==========================*/ @@ -860,3 +866,125 @@ gssd_destroy_krb5_machine_creds(void) krb5_free_context(context); } +#ifdef HAVE_SET_ALLOWABLE_ENCTYPES +/* + * this routine obtains a credentials handle via gss_acquire_cred() + * then calls gss_krb5_set_allowable_enctypes() to limit the encryption + * types negotiated. + * + * Returns: + * 0 => all went well + * -1 => there was an error + */ + +int +limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid) +{ + u_int maj_stat, min_stat; + gss_cred_id_t credh; + gss_OID_set_desc desired_mechs; + krb5_enctype enctypes[] = {ENCTYPE_DES_CBC_CRC}; + int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]); + + /* We only care about getting a krb5 cred */ + desired_mechs.count = 1; + desired_mechs.elements = &krb5oid; + + maj_stat = gss_acquire_cred(&min_stat, NULL, 0, + &desired_mechs, GSS_C_INITIATE, + &credh, NULL, NULL); + + if (maj_stat != GSS_S_COMPLETE) { + pgsserr("gss_acquire_cred", + maj_stat, min_stat, &krb5oid); + return -1; + } + + /* + * If we failed for any reason to produce global + * list of supported enctypes, use local default here. + */ + if (krb5_enctypes == NULL) + maj_stat = gss_set_allowable_enctypes(&min_stat, credh, + &krb5oid, num_enctypes, &enctypes); + else + maj_stat = gss_set_allowable_enctypes(&min_stat, credh, + &krb5oid, num_krb5_enctypes, + krb5_enctypes); + if (maj_stat != GSS_S_COMPLETE) { + pgsserr("gss_set_allowable_enctypes", + maj_stat, min_stat, &krb5oid); + return -1; + } + sec->cred = credh; + + return 0; +} +#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */ + +/* + * Obtain supported enctypes from kernel. + * Set defaults if info is not available. + */ +void +gssd_obtain_kernel_krb5_info(void) +{ + char enctype_file_name[128]; + char buf[1024]; + char enctypes[128]; + int nscanned; + int fd; + int use_default_enctypes = 0; + int nbytes, numfields; + char default_enctypes[] = "1,3,2"; + int code; + + snprintf(enctype_file_name, sizeof(enctype_file_name), + "%s/%s", pipefs_dir, "krb5_info"); + + if ((fd = open(enctype_file_name, O_RDONLY)) == -1) { + printerr(1, "WARNING: gssd_obtain_kernel_krb5_info: " + "Unable to open '%s'. Unable to determine " + "Kerberos encryption types supported by the " + "kernel; using defaults (%s).\n", + enctype_file_name, default_enctypes); + use_default_enctypes = 1; + goto do_the_parse; + } + memset(buf, 0, sizeof(buf)); + if ((nbytes = read(fd, buf, sizeof(buf)-1)) == -1) { + printerr(0, "WARNING: gssd_obtain_kernel_krb5_info: " + "Error reading Kerberos encryption type " + "information file '%s'; using defaults (%s).\n", + enctype_file_name, default_enctypes); + use_default_enctypes = 1; + close(fd); + goto do_the_parse; + } + close(fd); + numfields = sscanf(buf, "enctypes: %s\n%n", enctypes, &nscanned); + if (numfields < 1) { + printerr(0, "WARNING: gssd_obtain_kernel_krb5_info: " + "error parsing Kerberos encryption type " + "information from file '%s'; using defaults (%s).\n", + enctype_file_name, default_enctypes); + use_default_enctypes = 1; + goto do_the_parse; + } + if (nbytes > nscanned) { + printerr(2, "gssd_obtain_kernel_krb5_info: " + "Ignoring extra information, '%s', from '%s'\n", + buf+nscanned, enctype_file_name); + goto do_the_parse; + } + do_the_parse: + if (use_default_enctypes) + strcpy(enctypes, default_enctypes); + + if ((code = parse_enctypes(enctypes)) != 0) { + printerr(0, "ERROR: gssd_obtain_kernel_krb5_info: " + "parse_enctypes%s failed with code %d\n", + use_default_enctypes ? " (with default enctypes)" : "", + code); + } +} diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h index da04530..2af1a25 100644 --- a/utils/gssd/krb5_util.h +++ b/utils/gssd/krb5_util.h @@ -22,6 +22,8 @@ int gssd_refresh_krb5_machine_creds(voi void gssd_free_krb5_machine_cred_list(char **list); void gssd_setup_krb5_machine_gss_ccache(char *servername); void gssd_destroy_krb5_machine_creds(void); +void gssd_obtain_kernel_krb5_info(void); + #ifdef HAVE_SET_ALLOWABLE_ENCTYPES int limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid); diff --git a/utils/gssd/context.h b/utils/gssd/context.h index 67ed3bb..68b0c11 100644 --- a/utils/gssd/context.h +++ b/utils/gssd/context.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2004 The Regents of the University of Michigan. + Copyright (c) 2004-2006 The Regents of the University of Michigan. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -36,6 +36,10 @@ #include /* Hopefully big enough to hold any serialized context */ #define MAX_CTX_LEN 4096 +/* New context format flag values */ +#define KRB5_CTX_FLAG_INITIATOR 0x00000001 +#define KRB5_CTX_FLAG_CFX 0x00000002 +#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 int serialize_context_for_kernel(gss_ctx_id_t ctx, gss_buffer_desc *buf, gss_OID mech); diff --git a/utils/gssd/context_lucid.c b/utils/gssd/context_lucid.c index 3550762..c824dcd 100644 --- a/utils/gssd/context_lucid.c +++ b/utils/gssd/context_lucid.c @@ -40,6 +40,7 @@ #ifdef HAVE_LUCID_CONTEXT_SUPPORT #include #include #include +#include #include "gss_util.h" #include "gss_oids.h" #include "err_util.h" @@ -113,15 +114,13 @@ #ifdef HAVE_HEIMDAL * Note that the rfc1964 version only supports DES enctypes. */ if (lctx->rfc1964_kd.ctx_key.type != 4) { - printerr(1, "prepare_krb5_rfc1964_buffer: " - "overriding heimdal keytype (%d => %d)\n", - lctx->rfc1964_kd.ctx_key.type, 4); + printerr(2, "%s: overriding heimdal keytype (%d => %d)\n", + __FUNCTION__, lctx->rfc1964_kd.ctx_key.type, 4); lctx->rfc1964_kd.ctx_key.type = 4; } #endif - printerr(2, "prepare_krb5_rfc1964_buffer: serializing keys with " - "enctype %d and length %d\n", - lctx->rfc1964_kd.ctx_key.type, + printerr(2, "%s: serializing keys with enctype %d and length %d\n", + __FUNCTION__, lctx->rfc1964_kd.ctx_key.type, lctx->rfc1964_kd.ctx_key.length); /* derive the encryption key and copy it into buffer */ @@ -152,15 +151,361 @@ out_err: return -1; } -static int -prepare_krb5_rfc_cfx_buffer(gss_krb5_lucid_context_v1_t *lctx, - gss_buffer_desc *buf) +/* XXX Hack alert! XXX Do NOT submit upstream! XXX */ +/* XXX Hack alert! XXX Do NOT submit upstream! XXX */ + +/* for 3DES */ +#define KG_USAGE_SEAL 22 +#define KG_USAGE_SIGN 23 +#define KG_USAGE_SEQ 24 + +/* for rfc???? */ +#define KG_USAGE_ACCEPTOR_SEAL 22 +#define KG_USAGE_ACCEPTOR_SIGN 23 +#define KG_USAGE_INITIATOR_SEAL 24 +#define KG_USAGE_INITIATOR_SIGN 25 + +/* Lifted from mit src/lib/gssapi/krb5/gssapiP_krb5.h */ +enum seal_alg { + SEAL_ALG_NONE = 0xffff, + SEAL_ALG_DES = 0x0000, + SEAL_ALG_1 = 0x0001, /* not published */ + SEAL_ALG_MICROSOFT_RC4 = 0x0010, /* microsoft w2k; */ + SEAL_ALG_DES3KD = 0x0002 +}; + +#define KEY_USAGE_SEED_ENCRYPTION 0xAA +#define KEY_USAGE_SEED_INTEGRITY 0x55 +#define KEY_USAGE_SEED_CHECKSUM 0x99 +#define K5CLENGTH 5 + +/* Flags for version 2 context flags */ +#define KRB5_CTX_FLAG_INITIATOR 0x00000001 +#define KRB5_CTX_FLAG_CFX 0x00000002 +#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 + +/* XXX Hack alert! XXX Do NOT submit upstream! XXX */ +/* XXX Hack alert! XXX Do NOT submit upstream! XXX */ +/* + * We don't have "legal" access to these MIT-only + * structures located in libk5crypto + */ +extern void krb5int_enc_arcfour; +extern void krb5int_enc_des3; +extern void krb5int_enc_aes128; +extern void krb5int_enc_aes256; +extern int krb5_derive_key(); + +static void +key_lucid_to_krb5(const gss_krb5_lucid_key_t *lin, krb5_keyblock *kout) { - printerr(0, "ERROR: prepare_krb5_rfc_cfx_buffer: not implemented\n"); - return -1; + memset(kout, '\0', sizeof(kout)); +#ifdef HAVE_KRB5 + kout->enctype = lin->type; + kout->length = lin->length; + kout->contents = lin->data; +#else + kout->keytype = lin->type; + kout->keyvalue.length = lin->length; + kout->keyvalue.data = lin->data; +#endif +} + +static void +key_krb5_to_lucid(const krb5_keyblock *kin, gss_krb5_lucid_key_t *lout) +{ + memset(lout, '\0', sizeof(lout)); +#ifdef HAVE_KRB5 + lout->type = kin->enctype; + lout->length = kin->length; + lout->data = kin->contents; +#else + lout->type = kin->keytype; + lout->length = kin->keyvalue.length; + memcpy(lout->data, kin->keyvalue.data, kin->keyvalue.length); +#endif +} + +/* XXX Hack alert! XXX Do NOT submit upstream! XXX */ +/* XXX Hack alert! XXX Do NOT submit upstream! XXX */ +/* XXX Hack alert! XXX Do NOT submit upstream! XXX */ +/* XXX Hack alert! XXX Do NOT submit upstream! XXX */ +/* + * Function to derive a new key from a given key and given constant data. + */ +static krb5_error_code +derive_key_lucid(const gss_krb5_lucid_key_t *in, gss_krb5_lucid_key_t *out, + int usage, char extra) +{ + krb5_error_code code; + unsigned char constant_data[K5CLENGTH]; + krb5_data datain; + int keylength; + void *enc; + krb5_keyblock kin, kout; /* must send krb5_keyblock, not lucid! */ +#ifdef HAVE_HEIMDAL + krb5_context kcontext; + krb5_keyblock *outkey; +#endif + + /* + * XXX Hack alert. We don't have "legal" access to these + * values and structures located in libk5crypto + */ + switch (in->type) { + case ENCTYPE_DES3_CBC_SHA1: +#ifdef HAVE_KRB5 + case ENCTYPE_DES3_CBC_RAW: +#endif + keylength = 24; +#ifdef HAVE_KRB5 + enc = &krb5int_enc_des3; +#endif + break; + case ENCTYPE_AES128_CTS_HMAC_SHA1_96: + keylength = 16; +#ifdef HAVE_KRB5 + enc = &krb5int_enc_aes128; +#endif + break; + case ENCTYPE_AES256_CTS_HMAC_SHA1_96: + keylength = 32; +#ifdef HAVE_KRB5 + enc = &krb5int_enc_aes256; +#endif + break; + default: + code = KRB5_BAD_ENCTYPE; + goto out; + } + + /* allocate memory for output key */ + if ((out->data = malloc(keylength)) == NULL) { + code = ENOMEM; + goto out; + } + out->length = keylength; + out->type = in->type; + + /* Convert to correct format for call to krb5_derive_key */ + key_lucid_to_krb5(in, &kin); + key_lucid_to_krb5(out, &kout); + + datain.data = (char *) constant_data; + datain.length = K5CLENGTH; + + ((char *)(datain.data))[0] = (usage>>24)&0xff; + ((char *)(datain.data))[1] = (usage>>16)&0xff; + ((char *)(datain.data))[2] = (usage>>8)&0xff; + ((char *)(datain.data))[3] = usage&0xff; + + ((char *)(datain.data))[4] = (char) extra; + +#ifdef HAVE_KRB5 + code = krb5_derive_key(enc, &kin, &kout, &datain); +#else + if ((code = krb5_init_context(&kcontext))) { + } + code = krb5_derive_key(kcontext, &kin, in->type, constant_data, K5CLENGTH, &outkey); +#endif + if (code) { + free(out->data); + out->data = NULL; + goto out; + } +#ifdef HAVE_KRB5 + key_krb5_to_lucid(&kout, out); +#else + key_krb5_to_lucid(outkey, out); + krb5_free_keyblock(kcontext, outkey); + krb5_free_context(kcontext); +#endif + + out: + if (code) + printerr(0, "ERROR: %s: returning error %d (%s)\n", + __FUNCTION__, code, error_message(code)); + return (code); } +/* + * Prepare a new-style buffer, as defined in rfc4121 (a.k.a. cfx), + * to send to the kernel for newer encryption types -- or for DES3. + * + * The new format is: + * + * u32 initiate; ( whether we are the initiator or not ) + * s32 endtime; + * u32 flags; + * #define KRB5_CTX_FLAG_INITIATOR 0x00000001 + * #define KRB5_CTX_FLAG_CFX 0x00000002 + * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 + * u64 seq_send; + * u32 enctype; ( encrption type of keys ) + * u32 size_of_each_key; ( size of each key in bytes ) + * u32 number_of_keys; ( N -- should always be 3 for now ) + * keydata-1; ( Ke ) + * keydata-2; ( Ki ) + * keydata-3; ( Kc ) + * + */ +static int +prepare_krb5_rfc4121_buffer(gss_krb5_lucid_context_v1_t *lctx, + gss_buffer_desc *buf) +{ + char *p, *end; + uint32_t v2_flags = 0; + gss_krb5_lucid_key_t enc_key; + gss_krb5_lucid_key_t derived_key; + gss_buffer_desc fakeoid; + uint32_t enctype; + uint32_t keysize; + uint32_t numkeys; + + memset(&enc_key, 0, sizeof(enc_key)); + memset(&fakeoid, 0, sizeof(fakeoid)); + + if (!(buf->value = calloc(1, MAX_CTX_LEN))) + goto out_err; + p = buf->value; + end = buf->value + MAX_CTX_LEN; + + /* Version 2 */ + if (WRITE_BYTES(&p, end, lctx->initiate)) goto out_err; + if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err; + + if (lctx->initiate) + v2_flags |= KRB5_CTX_FLAG_INITIATOR; + if (lctx->protocol != 0) + v2_flags |= KRB5_CTX_FLAG_CFX; + if (lctx->protocol != 0 && lctx->cfx_kd.have_acceptor_subkey == 1) + v2_flags |= KRB5_CTX_FLAG_ACCEPTOR_SUBKEY; + + if (WRITE_BYTES(&p, end, v2_flags)) goto out_err; + + if (WRITE_BYTES(&p, end, lctx->send_seq)) goto out_err; + + /* Protocol 0 here implies DES3 or RC4 */ + printerr(2, "%s: protocol %d\n", __FUNCTION__, lctx->protocol); + if (lctx->protocol == 0) { + enctype = lctx->rfc1964_kd.ctx_key.type; +#ifdef HAVE_HEIMDAL + /* + * The kernel gss code expects ENCTYPE_DES3_CBC_RAW (6) for + * 3des keys, but Heimdal key has ENCTYPE_DES3_CBC_SHA1 (16). + * Force the Heimdal enctype to 6. + */ + if (enctype == ENCTYPE_DES3_CBC_SHA1) { + printerr(2, "%s: overriding heimdal keytype (%d => %d)\n", + __FUNCTION__, enctype, 6); + + enctype = 6; + } +#endif + keysize = lctx->rfc1964_kd.ctx_key.length; + numkeys = 3; /* XXX is always gonna be three? */ + } else { + if (lctx->cfx_kd.have_acceptor_subkey) { + enctype = lctx->cfx_kd.acceptor_subkey.type; + keysize = lctx->cfx_kd.acceptor_subkey.length; + } else { + enctype = lctx->cfx_kd.ctx_key.type; + keysize = lctx->cfx_kd.ctx_key.length; + } + numkeys = 3; + } + printerr(2, "%s: serializing %d keys with enctype %d and size %d\n", + __FUNCTION__, numkeys, enctype, keysize); + if (WRITE_BYTES(&p, end, enctype)) goto out_err; + if (WRITE_BYTES(&p, end, keysize)) goto out_err; + if (WRITE_BYTES(&p, end, numkeys)) goto out_err; + + if (lctx->protocol == 0) { + /* derive and send down: Ke, Ki, and Kc */ + /* Ke */ + if (write_bytes(&p, end, lctx->rfc1964_kd.ctx_key.data, + lctx->rfc1964_kd.ctx_key.length)) + goto out_err; + + /* Ki */ + if (write_bytes(&p, end, lctx->rfc1964_kd.ctx_key.data, + lctx->rfc1964_kd.ctx_key.length)) + goto out_err; + + /* Kc */ + if (derive_key_lucid(&lctx->rfc1964_kd.ctx_key, + &derived_key, + KG_USAGE_SIGN, KEY_USAGE_SEED_CHECKSUM)) + goto out_err; + if (write_bytes(&p, end, derived_key.data, + derived_key.length)) + goto out_err; + free(derived_key.data); + } else { + gss_krb5_lucid_key_t *keyptr; + uint32_t sign_usage, seal_usage; + + if (lctx->cfx_kd.have_acceptor_subkey) + keyptr = &lctx->cfx_kd.acceptor_subkey; + else + keyptr = &lctx->cfx_kd.ctx_key; + + if (lctx->initiate == 1) { + sign_usage = KG_USAGE_INITIATOR_SIGN; + seal_usage = KG_USAGE_INITIATOR_SEAL; + } else { + sign_usage = KG_USAGE_ACCEPTOR_SIGN; + seal_usage = KG_USAGE_ACCEPTOR_SEAL; + } + + /* derive and send down: Ke, Ki, and Kc */ + + /* Ke */ + if (derive_key_lucid(keyptr, &derived_key, + seal_usage, KEY_USAGE_SEED_ENCRYPTION)) + goto out_err; + if (write_bytes(&p, end, derived_key.data, + derived_key.length)) + goto out_err; + free(derived_key.data); + + /* Ki */ + if (derive_key_lucid(keyptr, &derived_key, + seal_usage, KEY_USAGE_SEED_INTEGRITY)) + goto out_err; + if (write_bytes(&p, end, derived_key.data, + derived_key.length)) + goto out_err; + free(derived_key.data); + + /* Kc */ + if (derive_key_lucid(keyptr, &derived_key, + sign_usage, KEY_USAGE_SEED_CHECKSUM)) + goto out_err; + if (write_bytes(&p, end, derived_key.data, + derived_key.length)) + goto out_err; + free(derived_key.data); + } + + buf->length = p - (char *)buf->value; + return 0; + +out_err: + printerr(0, "ERROR: %s: failed serializing krb5 context for kernel\n", + __FUNCTION__); + if (buf->value) { + free(buf->value); + buf->value = NULL; + } + buf->length = 0; + if (enc_key.data) { + free(enc_key.data); + enc_key.data = NULL; + } + return -1; +} int serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf) { @@ -170,7 +515,7 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss gss_krb5_lucid_context_v1_t *lctx = 0; int retcode = 0; - printerr(2, "DEBUG: serialize_krb5_ctx: lucid version!\n"); + printerr(2, "DEBUG: %s: lucid version!\n", __FUNCTION__); maj_stat = gss_export_lucid_sec_context(&min_stat, &ctx, 1, &return_ctx); if (maj_stat != GSS_S_COMPLETE) { @@ -192,11 +537,20 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss break; } - /* Now lctx points to a lucid context that we can send down to kernel */ - if (lctx->protocol == 0) + /* + * Now lctx points to a lucid context that we can send down to kernel + * + * Note: we send down different information to the kernel depending + * on the protocol version and the enctyption type. + * For protocol version 0 with all enctypes besides DES3, we use + * the original format. For protocol version != 0 or DES3, we + * send down the new style information. + */ + + if (lctx->protocol == 0 && lctx->rfc1964_kd.ctx_key.type <= 4) retcode = prepare_krb5_rfc1964_buffer(lctx, buf); else - retcode = prepare_krb5_rfc_cfx_buffer(lctx, buf); + retcode = prepare_krb5_rfc4121_buffer(lctx, buf); maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, return_ctx); if (maj_stat != GSS_S_COMPLETE) { @@ -206,8 +560,8 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss } if (retcode) { - printerr(1, "serialize_krb5_ctx: prepare_krb5_*_buffer " - "failed (retcode = %d)\n", retcode); + printerr(1, "%s: prepare_krb5_*_buffer failed (retcode = %d)\n", + __FUNCTION__, retcode); goto out_err; } @@ -217,4 +571,7 @@ out_err: printerr(0, "ERROR: failed serializing krb5 context for kernel\n"); return -1; } + + + #endif /* HAVE_LUCID_CONTEXT_SUPPORT */ diff --git a/utils/gssd/context_mit.c b/utils/gssd/context_mit.c index 94b2266..cd6ab0f 100644 --- a/utils/gssd/context_mit.c +++ b/utils/gssd/context_mit.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2004 The Regents of the University of Michigan. + Copyright (c) 2004-2006 The Regents of the University of Michigan. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -36,6 +36,7 @@ #ifdef HAVE_KRB5 #include #include #include +#include #include #include #include @@ -50,8 +51,7 @@ #if (KRB5_VERSION > 131) /* XXX argggg, there's gotta be a better way than just duplicating this * whole struct. Unfortunately, this is in a "private" header file, * so this is our best choice at this point :-/ - * - * XXX Does this match the Heimdal definition? */ + */ typedef struct _krb5_gss_ctx_id_rec { unsigned int initiate : 1; /* nonzero if initiating, zero if accepting */ @@ -139,6 +139,124 @@ write_keyblock(char **p, char *end, stru } /* + * XXX Hack alert! XXX Do NOT submit upstream! + * XXX Hack alert! XXX Do NOT submit upstream! + * + * We shouldn't be using these definitions + * + * XXX Hack alert! XXX Do NOT submit upstream! + * XXX Hack alert! XXX Do NOT submit upstream! + */ +/* for 3DES */ +#define KG_USAGE_SEAL 22 +#define KG_USAGE_SIGN 23 +#define KG_USAGE_SEQ 24 + +/* for rfc???? */ +#define KG_USAGE_ACCEPTOR_SEAL 22 +#define KG_USAGE_ACCEPTOR_SIGN 23 +#define KG_USAGE_INITIATOR_SEAL 24 +#define KG_USAGE_INITIATOR_SIGN 25 + +/* Lifted from mit src/lib/gssapi/krb5/gssapiP_krb5.h */ +enum seal_alg { + SEAL_ALG_NONE = 0xffff, + SEAL_ALG_DES = 0x0000, + SEAL_ALG_1 = 0x0001, /* not published */ + SEAL_ALG_MICROSOFT_RC4 = 0x0010, /* microsoft w2k; */ + SEAL_ALG_DES3KD = 0x0002 +}; + +#define KEY_USAGE_SEED_ENCRYPTION 0xAA +#define KEY_USAGE_SEED_INTEGRITY 0x55 +#define KEY_USAGE_SEED_CHECKSUM 0x99 +#define K5CLENGTH 5 + +extern void krb5_enc_des3; +extern void krb5int_enc_des3; +extern void krb5int_enc_arcfour; +extern void krb5int_enc_aes128; +extern void krb5int_enc_aes256; +extern int krb5_derive_key(); + +/* + * XXX Hack alert! XXX Do NOT submit upstream! + * XXX Hack alert! XXX Do NOT submit upstream! + * + * We should be passing down a single key to the kernel + * and it should be deriving the other keys. We cannot + * depend on any of this stuff being accessible in the + * future. + * + * XXX Hack alert! XXX Do NOT submit upstream! + * XXX Hack alert! XXX Do NOT submit upstream! + */ +/* + * Function to derive a new key from a given key and given constant data. + */ +static krb5_error_code +derive_key(const krb5_keyblock *in, krb5_keyblock *out, int usage, char extra) +{ + krb5_error_code code; + unsigned char constant_data[K5CLENGTH]; + krb5_data datain; + int keylength; + void *enc; + + switch (in->enctype) { +#ifdef ENCTYPE_DES3_CBC_RAW + case ENCTYPE_DES3_CBC_RAW: + keylength = 24; +/* Extra hack, the structure was renamed as rc4 was added... */ +#if defined(ENCTYPE_ARCFOUR_HMAC) + enc = &krb5int_enc_des3; +#else + enc = &krb5_enc_des3; +#endif + break; +#endif +#ifdef ENCTYPE_ARCFOUR_HMAC + case ENCTYPE_ARCFOUR_HMAC: + keylength = 16; + enc = &krb5int_enc_arcfour; + break; +#endif + default: + code = KRB5_BAD_ENCTYPE; + goto out; + } + + /* allocate memory for output key */ + if ((out->contents = malloc(keylength)) == NULL) { + code = ENOMEM; + goto out; + } + out->length = keylength; + out->enctype = in->enctype; + + datain.data = (char *) constant_data; + datain.length = K5CLENGTH; + + datain.data[0] = (usage>>24)&0xff; + datain.data[1] = (usage>>16)&0xff; + datain.data[2] = (usage>>8)&0xff; + datain.data[3] = usage&0xff; + + datain.data[4] = (char) extra; + + if ((code = krb5_derive_key(enc, in, out, &datain))) { + free(out->contents); + out->contents = NULL; + } + + out: + if (code) + printerr(0, "ERROR: derive_key returning error %d (%s)\n", + code, error_message(code)); + return (code); +} + +/* * We really shouldn't know about glue-layer context structure, but * we need to get at the real krb5 context pointer. This should be * removed as soon as we say there is no support for MIT Kerberos @@ -154,48 +272,124 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss { krb5_gss_ctx_id_t kctx = ((gss_union_ctx_id_t)ctx)->internal_ctx_id; char *p, *end; - static int constant_one = 1; static int constant_zero = 0; + static int constant_one = 1; + static int constant_two = 2; uint32_t word_seq_send; + u_int64_t seq_send_64bit; + uint32_t v2_flags = 0; + krb5_keyblock derived_key; + uint32_t numkeys; if (!(buf->value = calloc(1, MAX_CTX_LEN))) goto out_err; p = buf->value; end = buf->value + MAX_CTX_LEN; - if (kctx->initiate) { - if (WRITE_BYTES(&p, end, constant_one)) goto out_err; - } - else { - if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; - } - if (kctx->seed_init) { - if (WRITE_BYTES(&p, end, constant_one)) goto out_err; - } - else { - if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; - } - if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed))) - goto out_err; - if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err; - if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err; - if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err; - word_seq_send = kctx->seq_send; - if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err; - if (write_oid(&p, end, kctx->mech_used)) goto out_err; + switch (kctx->sealalg) { + case SEAL_ALG_DES: + /* Old format of context to the kernel */ + if (kctx->initiate) { + if (WRITE_BYTES(&p, end, constant_one)) goto out_err; + } + else { + if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; + } + if (kctx->seed_init) { + if (WRITE_BYTES(&p, end, constant_one)) goto out_err; + } + else { + if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; + } + if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed))) + goto out_err; + if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err; + if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err; + if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err; + word_seq_send = kctx->seq_send; + if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err; + if (write_oid(&p, end, kctx->mech_used)) goto out_err; + + printerr(2, "serialize_krb5_ctx: serializing keys with " + "enctype %d and length %d\n", + kctx->enc->enctype, kctx->enc->length); + + if (write_keyblock(&p, end, kctx->enc)) goto out_err; + if (write_keyblock(&p, end, kctx->seq)) goto out_err; + break; + case SEAL_ALG_MICROSOFT_RC4: + case SEAL_ALG_DES3KD: + /* New format of context to the kernel */ + /* s32 endtime; + * u32 flags; + * #define KRB5_CTX_FLAG_INITIATOR 0x00000001 + * #define KRB5_CTX_FLAG_CFX 0x00000002 + * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 + * u64 seq_send; + * u32 enctype; + * u32 size_of_each_key; ( size in bytes ) + * u32 number_of_keys; ( N (assumed to be 3 for now) ) + * keydata-1; ( Ke (Kenc for DES3) ) + * keydata-2; ( Ki (Kseq for DES3) ) + * keydata-3; ( Kc (derived checksum key) ) + */ + if (kctx->initiate) { + if (WRITE_BYTES(&p, end, constant_one)) goto out_err; + } + else { + if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; + } + if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err; + + /* Only applicable flag for this is initiator */ + if (kctx->initiate) v2_flags |= KRB5_CTX_FLAG_INITIATOR; + if (WRITE_BYTES(&p, end, v2_flags)) goto out_err; + + seq_send_64bit = kctx->seq_send; + if (WRITE_BYTES(&p, end, seq_send_64bit)) goto out_err; - printerr(2, "serialize_krb5_ctx: serializing keys with " - "enctype %d and length %d\n", - kctx->enc->enctype, kctx->enc->length); + if (WRITE_BYTES(&p, end, kctx->enc->enctype)) goto out_err; + if (WRITE_BYTES(&p, end, kctx->enc->length)) goto out_err; + numkeys = 3; + if (WRITE_BYTES(&p, end, numkeys)) goto out_err; + printerr(2, "serialize_krb5_ctx: serializing %d keys with " + "enctype %d and size %d\n", + numkeys, kctx->enc->enctype, kctx->enc->length); - if (write_keyblock(&p, end, kctx->enc)) goto out_err; - if (write_keyblock(&p, end, kctx->seq)) goto out_err; + /* Ke */ + if (write_bytes(&p, end, kctx->enc->contents, + kctx->enc->length)) + goto out_err; + + /* Ki */ + if (write_bytes(&p, end, kctx->enc->contents, + kctx->enc->length)) + goto out_err; + + /* Kc */ + if (derive_key(kctx->seq, &derived_key, + KG_USAGE_SIGN, KEY_USAGE_SEED_CHECKSUM)) + goto out_err; + if (write_bytes(&p, end, derived_key.contents, + derived_key.length)) + goto out_err; + free(derived_key.contents); + break; + default: + printerr(0, "ERROR: serialize_krb5_ctx: unsupported seal " + "algorithm %d\n", kctx->sealalg); + goto out_err; + } buf->length = p - (char *)buf->value; return 0; + out_err: printerr(0, "ERROR: failed serializing krb5 context for kernel\n"); - if (buf->value) free(buf->value); + if (buf->value) { + free(buf->value); + } + buf->value = NULL; buf->length = 0; return -1; }