patch-2.1.32 linux/net/sunrpc/svcauth_des.c
Next file: linux/net/sunrpc/svcsock.c
Previous file: linux/net/sunrpc/svcauth.c
Back to the patch index
Back to the overall index
- Lines: 216
- Date:
Fri Jan 3 04:10:28 1997
- Orig file:
v2.1.31/linux/net/sunrpc/svcauth_des.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/svcauth_des.c linux/net/sunrpc/svcauth_des.c
@@ -0,0 +1,215 @@
+/*
+ * linux/net/sunrpc/svcauth_des.c
+ *
+ * Server-side AUTH_DES handling.
+ *
+ * Copyright (C) 1996, 1997 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/sunrpc/types.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/svcauth.h>
+#include <linux/sunrpc/svcsock.h>
+
+#define RPCDBG_FACILITY RPCDBG_AUTH
+
+/*
+ * DES cedential cache.
+ * The cache is indexed by fullname/key to allow for multiple sessions
+ * by the same user from different hosts.
+ * It would be tempting to use the client's IP address rather than the
+ * conversation key as an index, but that could become problematic for
+ * multi-homed hosts that distribute traffic across their interfaces.
+ */
+struct des_cred {
+ struct des_cred * dc_next;
+ char * dc_fullname;
+ u32 dc_nickname;
+ des_cblock dc_key; /* conversation key */
+ des_cblock dc_xkey; /* encrypted conv. key */
+ des_key_schedule dc_keysched;
+};
+
+#define ADN_FULLNAME 0
+#define ADN_NICKNAME 1
+
+/*
+ * The default slack allowed when checking for replayed credentials
+ * (in milliseconds).
+ */
+#define DES_REPLAY_SLACK 2000
+
+/*
+ * Make sure we don't place more than one call to the key server at
+ * a time.
+ */
+static int in_keycall = 0;
+
+#define FAIL(err) \
+ { if (data) put_cred(data); \
+ *authp = rpc_autherr_##err; \
+ return; \
+ }
+
+void
+svcauth_des(struct svc_rqst *rqstp, u32 *statp, u32 *authp)
+{
+ struct svc_buf *argp = &rqstp->rq_argbuf;
+ struct svc_buf *resp = &rqstp->rq_resbuf;
+ struct svc_cred *cred = &rqstp->rq_cred;
+ struct des_cred *data = NULL;
+ u32 cryptkey[2];
+ u32 cryptbuf[4];
+ u32 *p = argp->buf;
+ int len = argp->len, slen, i;
+
+ *authp = rpc_auth_ok;
+
+ if ((argp->len -= 3) < 0) {
+ *statp = rpc_garbage_args;
+ return;
+ }
+
+ p++; /* skip length field */
+ namekind = ntohl(*p++); /* fullname/nickname */
+
+ /* Get the credentials */
+ if (namekind == ADN_NICKNAME) {
+ /* If we can't find the cached session key, initiate a
+ * new session. */
+ if (!(data = get_cred_bynick(*p++)))
+ FAIL(rejectedcred);
+ } else if (namekind == ADN_FULLNAME) {
+ p = xdr_decode_string(p, &fullname, &len, RPC_MAXNETNAMELEN);
+ if (p == NULL)
+ FAIL(badcred);
+ cryptkey[0] = *p++; /* get the encrypted key */
+ cryptkey[1] = *p++;
+ cryptbuf[2] = *p++; /* get the encrypted window */
+ } else {
+ FAIL(badcred);
+ }
+
+ /* If we're just updating the key, silently discard the request. */
+ if (data && data->dc_locked) {
+ *authp = rpc_autherr_dropit;
+ _put_cred(data); /* release but don't unlock */
+ return;
+ }
+
+ /* Get the verifier flavor and length */
+ if (ntohl(*p++) != RPC_AUTH_DES && ntohl(*p++) != 12)
+ FAIL(badverf);
+
+ cryptbuf[0] = *p++; /* encrypted time stamp */
+ cryptbuf[1] = *p++;
+ cryptbuf[3] = *p++; /* 0 or window - 1 */
+
+ if (namekind == ADN_NICKNAME) {
+ status = des_ecb_encrypt((des_block *) cryptbuf,
+ (des_block *) cryptbuf,
+ data->dc_keysched, DES_DECRYPT);
+ } else {
+ /* We first have to decrypt the new session key and
+ * fill in the UNIX creds. */
+ if (!(data = get_cred_byname(rqstp, authp, fullname, cryptkey)))
+ return;
+ status = des_cbc_encrypt((des_cblock *) cryptbuf,
+ (des_cblock *) cryptbuf, 16,
+ data->dc_keysched,
+ (des_cblock *) &ivec,
+ DES_DECRYPT);
+ }
+ if (status) {
+ printk("svcauth_des: DES decryption failed (status %d)\n",
+ status);
+ FAIL(badverf);
+ }
+
+ /* Now check the whole lot */
+ if (namekind == ADN_FULLNAME) {
+ unsigned long winverf;
+
+ data->dc_window = ntohl(cryptbuf[2]);
+ winverf = ntohl(cryptbuf[2]);
+ if (window != winverf - 1) {
+ printk("svcauth_des: bad window verifier!\n");
+ FAIL(badverf);
+ }
+ }
+
+ /* XDR the decrypted timestamp */
+ cryptbuf[0] = ntohl(cryptbuf[0]);
+ cryptbuf[1] = ntohl(cryptbuf[1]);
+ if (cryptbuf[1] > 1000000) {
+ dprintk("svcauth_des: bad usec value %u\n", cryptbuf[1]);
+ if (namekind == ADN_NICKNAME)
+ FAIL(rejectedverf);
+ FAIL(badverf);
+ }
+
+ /*
+ * Check for replayed credentials. We must allow for reordering
+ * of requests by the network, and the OS scheduler, hence we
+ * cannot expect timestamps to be increasing monotonically.
+ * This opens a small security hole, therefore the replay_slack
+ * value shouldn't be too large.
+ */
+ if ((delta = cryptbuf[0] - data->dc_timestamp[0]) <= 0) {
+ switch (delta) {
+ case -1:
+ delta = -1000000;
+ case 0:
+ delta += cryptbuf[1] - data->dc_timestamp[1];
+ break;
+ default:
+ delta = -1000000;
+ }
+ if (delta < DES_REPLAY_SLACK)
+ FAIL(rejectedverf);
+#ifdef STRICT_REPLAY_CHECKS
+ /* TODO: compare time stamp to last five timestamps cached
+ * and reject (drop?) request if a match is found. */
+#endif
+ }
+
+ now = xtime;
+ now.tv_secs -= data->dc_window;
+ if (now.tv_secs < cryptbuf[0] ||
+ (now.tv_secs == cryptbuf[0] && now.tv_usec < cryptbuf[1]))
+ FAIL(rejectedverf);
+
+ /* Okay, we're done. Update the lot */
+ if (namekind == ADN_FULLNAME)
+ data->dc_valid = 1;
+ data->dc_timestamp[0] = cryptbuf[0];
+ data->dc_timestamp[1] = cryptbuf[1];
+
+ put_cred(data);
+ return;
+garbage:
+ *statp = rpc_garbage_args;
+ return;
+}
+
+/*
+ * Call the keyserver to obtain the decrypted conversation key and
+ * UNIX creds. We use a Linux-specific keycall extension that does
+ * both things in one go.
+ */
+static struct des_cred *
+get_cred_byname(struct svc_rqst *rqstp, u32 *authp, char *fullname, u32 *cryptkey)
+{
+ static int in_keycall = 0;
+ struct des_cred *cred;
+
+ if (in_keycall) {
+ *authp = rpc_autherr_dropit;
+ return NULL;
+ }
+ in_keycall = 1;
+ in_keycall = 0;
+ return cred;
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov