rpc: spkm3 update
authorOlga Kornievskaia <aglo@citi.umich.edu>
Tue, 5 Dec 2006 01:22:34 +0000 (20:22 -0500)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 6 Dec 2006 15:46:44 +0000 (10:46 -0500)
This updates the spkm3 code to bring it up to date with our current
understanding of the spkm3 spec.

In doing so, we're changing the downcall format used by gssd in the spkm3 case,
which will cause an incompatilibity with old userland spkm3 support.  Since the
old code a) didn't implement the protocol correctly, and b) was never
distributed except in the form of some experimental patches from the citi web
site, we're assuming this is OK.

We do detect the old downcall format and print warning (and fail).  We also
include a version number in the new downcall format, to be used in the
future in case any further change is required.

In some more detail:

- fix integrity support
- removed dependency on NIDs. instead OIDs are used
- known OID values for algorithms added.
- fixed some context fields and types

Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
include/linux/sunrpc/gss_spkm3.h
net/sunrpc/auth_gss/auth_gss.c
net/sunrpc/auth_gss/gss_spkm3_mech.c
net/sunrpc/auth_gss/gss_spkm3_seal.c
net/sunrpc/auth_gss/gss_spkm3_token.c
net/sunrpc/auth_gss/gss_spkm3_unseal.c

index 2cf3fbb..e3e6a34 100644 (file)
 #include <linux/sunrpc/gss_asn1.h>
 
 struct spkm3_ctx {
-       struct xdr_netobj       ctx_id; /* per message context id */
-       int                     qop;         /* negotiated qop */
+       struct xdr_netobj       ctx_id;  /* per message context id */
+       int                     endtime; /* endtime of the context */
        struct xdr_netobj       mech_used;
        unsigned int            ret_flags ;
-       unsigned int            req_flags ;
-       struct xdr_netobj       share_key;
-       int                     conf_alg;
-       struct crypto_blkcipher *derived_conf_key;
-       int                     intg_alg;
-       struct crypto_blkcipher *derived_integ_key;
-       int                     keyestb_alg;   /* alg used to get share_key */
-       int                     owf_alg;   /* one way function */
+       struct xdr_netobj       conf_alg;
+       struct xdr_netobj       derived_conf_key;
+       struct xdr_netobj       intg_alg;
+       struct xdr_netobj       derived_integ_key;
 };
 
-/* from openssl/objects.h */
-/* XXX need SEAL_ALG_NONE */
-#define NID_md5                4
-#define NID_dhKeyAgreement     28 
-#define NID_des_cbc            31 
-#define NID_sha1               64
-#define NID_cast5_cbc          108
+/* OIDs declarations for K-ALG, I-ALG, C-ALG, and OWF-ALG */
+extern const struct xdr_netobj hmac_md5_oid;
+extern const struct xdr_netobj cast5_cbc_oid;
 
 /* SPKM InnerContext Token types */
 
@@ -46,11 +38,13 @@ u32 spkm3_make_token(struct spkm3_ctx *ctx, struct xdr_buf * text, struct xdr_ne
 u32 spkm3_read_token(struct spkm3_ctx *ctx, struct xdr_netobj *read_token, struct xdr_buf *message_buffer, int toktype);
 
 #define CKSUMTYPE_RSA_MD5            0x0007
+#define CKSUMTYPE_HMAC_MD5           0x0008
 
-s32 make_checksum(s32 cksumtype, char *header, int hdrlen, struct xdr_buf *body,
-                   int body_offset, struct xdr_netobj *cksum);
+s32 make_spkm3_checksum(s32 cksumtype, struct xdr_netobj *key, char *header,
+               unsigned int hdrlen, struct xdr_buf *body,
+               unsigned int body_offset, struct xdr_netobj *cksum);
 void asn1_bitstring_len(struct xdr_netobj *in, int *enclen, int *zerobits);
-int decode_asn1_bitstring(struct xdr_netobj *out, char *in, int enclen, 
+int decode_asn1_bitstring(struct xdr_netobj *out, char *in, int enclen,
                    int explen);
 void spkm3_mic_header(unsigned char **hdrbuf, unsigned int *hdrlen, 
                    unsigned char *ctxhdr, int elen, int zbit);
index d12ee5f..a02ecc1 100644 (file)
@@ -68,7 +68,7 @@ static struct rpc_credops gss_credops;
 #define GSS_CRED_SLACK         1024            /* XXX: unused */
 /* length of a krb5 verifier (48), plus data added before arguments when
  * using integrity (two 4-byte integers): */
-#define GSS_VERF_SLACK         56
+#define GSS_VERF_SLACK         100
 
 /* XXX this define must match the gssd define
 * as it is passed to gssd to signal the use of
index d57f608..4146507 100644 (file)
@@ -82,133 +82,73 @@ simple_get_netobj(const void *p, const void *end, struct xdr_netobj *res)
        return q;
 }
 
-static inline const void *
-get_key(const void *p, const void *end, struct crypto_blkcipher **res,
-       int *resalg)
-{
-       struct xdr_netobj       key = { 0 };
-       int                     setkey = 0;
-       char                    *alg_name;
-
-       p = simple_get_bytes(p, end, resalg, sizeof(*resalg));
-       if (IS_ERR(p))
-               goto out_err;
-       p = simple_get_netobj(p, end, &key);
-       if (IS_ERR(p))
-               goto out_err;
-
-       switch (*resalg) {
-               case NID_des_cbc:
-                       alg_name = "cbc(des)";
-                       setkey = 1;
-                       break;
-               case NID_cast5_cbc:
-                       /* XXXX here in name only, not used */
-                       alg_name = "cbc(cast5)";
-                       setkey = 0; /* XXX will need to set to 1 */
-                       break;
-               case NID_md5:
-                       if (key.len == 0) {
-                               dprintk("RPC: SPKM3 get_key: NID_md5 zero Key length\n");
-                       }
-                       alg_name = "md5";
-                       setkey = 0;
-                       break;
-               default:
-                       dprintk("gss_spkm3_mech: unsupported algorithm %d\n", *resalg);
-                       goto out_err_free_key;
-       }
-       *res = crypto_alloc_blkcipher(alg_name, 0, CRYPTO_ALG_ASYNC);
-       if (IS_ERR(*res)) {
-               printk("gss_spkm3_mech: unable to initialize crypto algorthm %s\n", alg_name);
-               *res = NULL;
-               goto out_err_free_key;
-       }
-       if (setkey) {
-               if (crypto_blkcipher_setkey(*res, key.data, key.len)) {
-                       printk("gss_spkm3_mech: error setting key for crypto algorthm %s\n", alg_name);
-                       goto out_err_free_tfm;
-               }
-       }
-
-       if(key.len > 0)
-               kfree(key.data);
-       return p;
-
-out_err_free_tfm:
-       crypto_free_blkcipher(*res);
-out_err_free_key:
-       if(key.len > 0)
-               kfree(key.data);
-       p = ERR_PTR(-EINVAL);
-out_err:
-       return p;
-}
-
 static int
 gss_import_sec_context_spkm3(const void *p, size_t len,
                                struct gss_ctx *ctx_id)
 {
        const void *end = (const void *)((const char *)p + len);
        struct  spkm3_ctx *ctx;
+       int     version;
 
        if (!(ctx = kzalloc(sizeof(*ctx), GFP_KERNEL)))
                goto out_err;
 
+       p = simple_get_bytes(p, end, &version, sizeof(version));
+       if (IS_ERR(p))
+               goto out_err_free_ctx;
+       if (version != 1) {
+               dprintk("RPC: unknown spkm3 token format: obsolete nfs-utils?\n");
+               goto out_err_free_ctx;
+       }
+
        p = simple_get_netobj(p, end, &ctx->ctx_id);
        if (IS_ERR(p))
                goto out_err_free_ctx;
 
-       p = simple_get_bytes(p, end, &ctx->qop, sizeof(ctx->qop));
+       p = simple_get_bytes(p, end, &ctx->endtime, sizeof(ctx->endtime));
        if (IS_ERR(p))
                goto out_err_free_ctx_id;
 
        p = simple_get_netobj(p, end, &ctx->mech_used);
        if (IS_ERR(p))
-               goto out_err_free_mech;
+               goto out_err_free_ctx_id;
 
        p = simple_get_bytes(p, end, &ctx->ret_flags, sizeof(ctx->ret_flags));
        if (IS_ERR(p))
                goto out_err_free_mech;
 
-       p = simple_get_bytes(p, end, &ctx->req_flags, sizeof(ctx->req_flags));
+       p = simple_get_netobj(p, end, &ctx->conf_alg);
        if (IS_ERR(p))
                goto out_err_free_mech;
 
-       p = simple_get_netobj(p, end, &ctx->share_key);
-       if (IS_ERR(p))
-               goto out_err_free_s_key;
-
-       p = get_key(p, end, &ctx->derived_conf_key, &ctx->conf_alg);
+       p = simple_get_netobj(p, end, &ctx->derived_conf_key);
        if (IS_ERR(p))
-               goto out_err_free_s_key;
+               goto out_err_free_conf_alg;
 
-       p = get_key(p, end, &ctx->derived_integ_key, &ctx->intg_alg);
+       p = simple_get_netobj(p, end, &ctx->intg_alg);
        if (IS_ERR(p))
-               goto out_err_free_key1;
+               goto out_err_free_conf_key;
 
-       p = simple_get_bytes(p, end, &ctx->keyestb_alg, sizeof(ctx->keyestb_alg));
+       p = simple_get_netobj(p, end, &ctx->derived_integ_key);
        if (IS_ERR(p))
-               goto out_err_free_key2;
-
-       p = simple_get_bytes(p, end, &ctx->owf_alg, sizeof(ctx->owf_alg));
-       if (IS_ERR(p))
-               goto out_err_free_key2;
+               goto out_err_free_intg_alg;
 
        if (p != end)
-               goto out_err_free_key2;
+               goto out_err_free_intg_key;
 
        ctx_id->internal_ctx_id = ctx;
 
        dprintk("Successfully imported new spkm context.\n");
        return 0;
 
-out_err_free_key2:
-       crypto_free_blkcipher(ctx->derived_integ_key);
-out_err_free_key1:
-       crypto_free_blkcipher(ctx->derived_conf_key);
-out_err_free_s_key:
-       kfree(ctx->share_key.data);
+out_err_free_intg_key:
+       kfree(ctx->derived_integ_key.data);
+out_err_free_intg_alg:
+       kfree(ctx->intg_alg.data);
+out_err_free_conf_key:
+       kfree(ctx->derived_conf_key.data);
+out_err_free_conf_alg:
+       kfree(ctx->conf_alg.data);
 out_err_free_mech:
        kfree(ctx->mech_used.data);
 out_err_free_ctx_id:
@@ -220,13 +160,16 @@ out_err:
 }
 
 static void
-gss_delete_sec_context_spkm3(void *internal_ctx) {
+gss_delete_sec_context_spkm3(void *internal_ctx)
+{
        struct spkm3_ctx *sctx = internal_ctx;
 
-       crypto_free_blkcipher(sctx->derived_integ_key);
-       crypto_free_blkcipher(sctx->derived_conf_key);
-       kfree(sctx->share_key.data);
+       kfree(sctx->derived_integ_key.data);
+       kfree(sctx->intg_alg.data);
+       kfree(sctx->derived_conf_key.data);
+       kfree(sctx->conf_alg.data);
        kfree(sctx->mech_used.data);
+       kfree(sctx->ctx_id.data);
        kfree(sctx);
 }
 
@@ -238,7 +181,6 @@ gss_verify_mic_spkm3(struct gss_ctx         *ctx,
        u32 maj_stat = 0;
        struct spkm3_ctx *sctx = ctx->internal_ctx_id;
 
-       dprintk("RPC: gss_verify_mic_spkm3 calling spkm3_read_token\n");
        maj_stat = spkm3_read_token(sctx, checksum, signbuf, SPKM_MIC_TOK);
 
        dprintk("RPC: gss_verify_mic_spkm3 returning %d\n", maj_stat);
@@ -253,10 +195,9 @@ gss_get_mic_spkm3(struct gss_ctx   *ctx,
        u32 err = 0;
        struct spkm3_ctx *sctx = ctx->internal_ctx_id;
 
-       dprintk("RPC: gss_get_mic_spkm3\n");
-
        err = spkm3_make_token(sctx, message_buffer,
-                             message_token, SPKM_MIC_TOK);
+                               message_token, SPKM_MIC_TOK);
+       dprintk("RPC: gss_get_mic_spkm3 returning %d\n", err);
        return err;
 }
 
index 18c7862..b179d58 100644 (file)
 #include <linux/sunrpc/gss_spkm3.h>
 #include <linux/random.h>
 #include <linux/crypto.h>
+#include <linux/pagemap.h>
+#include <linux/scatterlist.h>
+#include <linux/sunrpc/xdr.h>
 
 #ifdef RPC_DEBUG
 # define RPCDBG_FACILITY        RPCDBG_AUTH
 #endif
 
+const struct xdr_netobj hmac_md5_oid = { 8, "\x2B\x06\x01\x05\x05\x08\x01\x01"};
+const struct xdr_netobj cast5_cbc_oid = {9, "\x2A\x86\x48\x86\xF6\x7D\x07\x42\x0A"};
+
 /*
  * spkm3_make_token()
  *
@@ -66,29 +72,23 @@ spkm3_make_token(struct spkm3_ctx *ctx,
        int                     ctxelen = 0, ctxzbit = 0;
        int                     md5elen = 0, md5zbit = 0;
 
-       dprintk("RPC: spkm3_make_token\n");
-
        now = jiffies;
 
        if (ctx->ctx_id.len != 16) {
                dprintk("RPC: spkm3_make_token BAD ctx_id.len %d\n",
-                       ctx->ctx_id.len);
+                               ctx->ctx_id.len);
                goto out_err;
        }
-               
-       switch (ctx->intg_alg) {
-               case NID_md5:
-                       checksum_type = CKSUMTYPE_RSA_MD5;
-                       break;
-               default:
-                       dprintk("RPC: gss_spkm3_seal: ctx->signalg %d not"
-                               " supported\n", ctx->intg_alg);
-                       goto out_err;
-       }
-       /* XXX since we don't support WRAP, perhaps we don't care... */
-       if (ctx->conf_alg != NID_cast5_cbc) {
-               dprintk("RPC: gss_spkm3_seal: ctx->sealalg %d not supported\n",
-                       ctx->conf_alg);
+
+       if (!g_OID_equal(&ctx->intg_alg, &hmac_md5_oid)) {
+               dprintk("RPC: gss_spkm3_seal: unsupported I-ALG algorithm."
+                               "only support hmac-md5 I-ALG.\n");
+               goto out_err;
+       } else
+               checksum_type = CKSUMTYPE_HMAC_MD5;
+
+       if (!g_OID_equal(&ctx->conf_alg, &cast5_cbc_oid)) {
+               dprintk("RPC: gss_spkm3_seal: unsupported C-ALG algorithm\n");
                goto out_err;
        }
 
@@ -96,10 +96,10 @@ spkm3_make_token(struct spkm3_ctx *ctx,
                /* Calculate checksum over the mic-header */
                asn1_bitstring_len(&ctx->ctx_id, &ctxelen, &ctxzbit);
                spkm3_mic_header(&mic_hdr.data, &mic_hdr.len, ctx->ctx_id.data,
-                                        ctxelen, ctxzbit);
-
-               if (make_checksum(checksum_type, mic_hdr.data, mic_hdr.len, 
-                                            text, 0, &md5cksum))
+                               ctxelen, ctxzbit);
+               if (make_spkm3_checksum(checksum_type, &ctx->derived_integ_key,
+                                       (char *)mic_hdr.data, mic_hdr.len,
+                                       text, 0, &md5cksum))
                        goto out_err;
 
                asn1_bitstring_len(&md5cksum, &md5elen, &md5zbit);
@@ -121,7 +121,66 @@ spkm3_make_token(struct spkm3_ctx *ctx,
 
        return  GSS_S_COMPLETE;
 out_err:
+       if (md5cksum.data)
+               kfree(md5cksum.data);
+
        token->data = NULL;
        token->len = 0;
        return GSS_S_FAILURE;
 }
+
+static int
+spkm3_checksummer(struct scatterlist *sg, void *data)
+{
+       struct hash_desc *desc = data;
+
+       return crypto_hash_update(desc, sg, sg->length);
+}
+
+/* checksum the plaintext data and hdrlen bytes of the token header */
+s32
+make_spkm3_checksum(s32 cksumtype, struct xdr_netobj *key, char *header,
+                   unsigned int hdrlen, struct xdr_buf *body,
+                   unsigned int body_offset, struct xdr_netobj *cksum)
+{
+       char                            *cksumname;
+       struct hash_desc                desc; /* XXX add to ctx? */
+       struct scatterlist              sg[1];
+       int err;
+
+       switch (cksumtype) {
+               case CKSUMTYPE_HMAC_MD5:
+                       cksumname = "md5";
+                       break;
+               default:
+                       dprintk("RPC:      spkm3_make_checksum:"
+                                       " unsupported checksum %d", cksumtype);
+                       return GSS_S_FAILURE;
+       }
+
+       if (key->data == NULL || key->len <= 0) return GSS_S_FAILURE;
+
+       desc.tfm = crypto_alloc_hash(cksumname, 0, CRYPTO_ALG_ASYNC);
+       if (IS_ERR(desc.tfm))
+               return GSS_S_FAILURE;
+       cksum->len = crypto_hash_digestsize(desc.tfm);
+       desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+       err = crypto_hash_setkey(desc.tfm, key->data, key->len);
+       if (err)
+               goto out;
+
+       sg_set_buf(sg, header, hdrlen);
+       crypto_hash_update(&desc, sg, 1);
+
+       xdr_process_buf(body, body_offset, body->len - body_offset,
+                       spkm3_checksummer, &desc);
+       crypto_hash_final(&desc, cksum->data);
+
+out:
+       crypto_free_hash(desc.tfm);
+
+       return err ? GSS_S_FAILURE : 0;
+}
+
+EXPORT_SYMBOL(make_spkm3_checksum);
index 854a983..35188b6 100644 (file)
@@ -172,10 +172,10 @@ spkm3_mic_header(unsigned char **hdrbuf, unsigned int *hdrlen, unsigned char *ct
        *(u8 *)hptr++ = zbit;
        memcpy(hptr, ctxdata, elen);
        hptr += elen;
-       *hdrlen = hptr - top; 
+       *hdrlen = hptr - top;
 }
-               
-/* 
+
+/*
  * spkm3_mic_innercontext_token()
  *
  * *tokp points to the beginning of the SPKM_MIC token  described 
index 544eb6f..e54581c 100644 (file)
@@ -54,66 +54,70 @@ spkm3_read_token(struct spkm3_ctx *ctx,
                struct xdr_buf *message_buffer, /* signbuf */
                int toktype)
 {
+       s32                     checksum_type;
        s32                     code;
        struct xdr_netobj       wire_cksum = {.len =0, .data = NULL};
        char                    cksumdata[16];
        struct xdr_netobj       md5cksum = {.len = 0, .data = cksumdata};
        unsigned char           *ptr = (unsigned char *)read_token->data;
-       unsigned char           *cksum;
+       unsigned char           *cksum;
        int                     bodysize, md5elen;
        int                     mic_hdrlen;
        u32                     ret = GSS_S_DEFECTIVE_TOKEN;
 
-       dprintk("RPC: spkm3_read_token read_token->len %d\n", read_token->len);
-
        if (g_verify_token_header((struct xdr_netobj *) &ctx->mech_used,
                                        &bodysize, &ptr, read_token->len))
                goto out;
 
        /* decode the token */
 
-       if (toktype == SPKM_MIC_TOK) {
-
-               if ((ret = spkm3_verify_mic_token(&ptr, &mic_hdrlen, &cksum))) 
-                       goto out;
-
-               if (*cksum++ != 0x03) {
-                       dprintk("RPC: spkm3_read_token BAD checksum type\n");
-                       goto out;
-               }
-               md5elen = *cksum++; 
-               cksum++;        /* move past the zbit */
-       
-               if(!decode_asn1_bitstring(&wire_cksum, cksum, md5elen - 1, 16))
-                       goto out;
-
-               /* HARD CODED FOR MD5 */
-
-               /* compute the checksum of the message.
-               *  ptr + 2 = start of header piece of checksum
-               *  mic_hdrlen + 2 = length of header piece of checksum
-               */
-               ret = GSS_S_DEFECTIVE_TOKEN;
-               code = make_checksum(CKSUMTYPE_RSA_MD5, ptr + 2, 
-                                       mic_hdrlen + 2, 
-                                       message_buffer, 0, &md5cksum);
-
-               if (code)
-                       goto out;
-
-               dprintk("RPC: spkm3_read_token: digest wire_cksum.len %d:\n", 
-                       wire_cksum.len);
-
-               ret = GSS_S_BAD_SIG;
-               code = memcmp(md5cksum.data, wire_cksum.data, wire_cksum.len);
-               if (code)
-                       goto out;
-
-       } else { 
-               dprintk("RPC: BAD or UNSUPPORTED SPKM3 token type: %d\n",toktype);
+       if (toktype != SPKM_MIC_TOK) {
+               dprintk("RPC: BAD SPKM3 token type: %d\n", toktype);
+               goto out;
+       }
+
+       if ((ret = spkm3_verify_mic_token(&ptr, &mic_hdrlen, &cksum)))
+               goto out;
+
+       if (*cksum++ != 0x03) {
+               dprintk("RPC: spkm3_read_token BAD checksum type\n");
+               goto out;
+       }
+       md5elen = *cksum++;
+       cksum++;        /* move past the zbit */
+
+       if (!decode_asn1_bitstring(&wire_cksum, cksum, md5elen - 1, 16))
+               goto out;
+
+       /* HARD CODED FOR MD5 */
+
+       /* compute the checksum of the message.
+        * ptr + 2 = start of header piece of checksum
+        * mic_hdrlen + 2 = length of header piece of checksum
+        */
+       ret = GSS_S_DEFECTIVE_TOKEN;
+       if (!g_OID_equal(&ctx->intg_alg, &hmac_md5_oid)) {
+               dprintk("RPC: gss_spkm3_seal: unsupported I-ALG algorithm\n");
+               goto out;
+       }
+
+       checksum_type = CKSUMTYPE_HMAC_MD5;
+
+       code = make_spkm3_checksum(checksum_type,
+               &ctx->derived_integ_key, ptr + 2, mic_hdrlen + 2,
+               message_buffer, 0, &md5cksum);
+
+       if (code)
+               goto out;
+
+       ret = GSS_S_BAD_SIG;
+       code = memcmp(md5cksum.data, wire_cksum.data, wire_cksum.len);
+       if (code) {
+               dprintk("RPC: bad MIC checksum\n");
                goto out;
        }
 
+
        /* XXX: need to add expiration and sequencing */
        ret = GSS_S_COMPLETE;
 out: