summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--net/rxrpc/ar-key.c109
1 files changed, 109 insertions, 0 deletions
diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c
index a3a7acb5071..bf4d623ee1c 100644
--- a/net/rxrpc/ar-key.c
+++ b/net/rxrpc/ar-key.c
@@ -29,6 +29,7 @@ static int rxrpc_instantiate_s(struct key *, const void *, size_t);
static void rxrpc_destroy(struct key *);
static void rxrpc_destroy_s(struct key *);
static void rxrpc_describe(const struct key *, struct seq_file *);
+static long rxrpc_read(const struct key *, char __user *, size_t);
/*
* rxrpc defined keys take an arbitrary string as the description and an
@@ -40,6 +41,7 @@ struct key_type key_type_rxrpc = {
.match = user_match,
.destroy = rxrpc_destroy,
.describe = rxrpc_describe,
+ .read = rxrpc_read,
};
EXPORT_SYMBOL(key_type_rxrpc);
@@ -592,3 +594,110 @@ struct key *rxrpc_get_null_key(const char *keyname)
return key;
}
EXPORT_SYMBOL(rxrpc_get_null_key);
+
+/*
+ * read the contents of an rxrpc key
+ * - this returns the result in XDR form
+ */
+static long rxrpc_read(const struct key *key,
+ char __user *buffer, size_t buflen)
+{
+ struct rxrpc_key_token *token;
+ size_t size, toksize;
+ __be32 __user *xdr;
+ u32 cnlen, tktlen, ntoks, zero;
+
+ _enter("");
+
+ /* we don't know what form we should return non-AFS keys in */
+ if (memcmp(key->description, "afs@", 4) != 0)
+ return -EOPNOTSUPP;
+ cnlen = strlen(key->description + 4);
+
+ /* AFS keys we return in XDR form, so we need to work out the size of
+ * the XDR */
+ size = 2 * 4; /* flags, cellname len */
+ size += (cnlen + 3) & ~3; /* cellname */
+ size += 1 * 4; /* token count */
+
+ ntoks = 0;
+ for (token = key->payload.data; token; token = token->next) {
+ switch (token->security_index) {
+ case RXRPC_SECURITY_RXKAD:
+ size += 2 * 4; /* length, security index (switch ID) */
+ size += 8 * 4; /* viceid, kvno, key*2, begin, end,
+ * primary, tktlen */
+ size += (token->kad->ticket_len + 3) & ~3; /* ticket */
+ ntoks++;
+ break;
+
+ default: /* can't encode */
+ break;
+ }
+ }
+
+ if (!buffer || buflen < size)
+ return size;
+
+ xdr = (__be32 __user *) buffer;
+ zero = 0;
+#define ENCODE(x) \
+ do { \
+ __be32 y = htonl(x); \
+ if (put_user(y, xdr++) < 0) \
+ goto fault; \
+ } while(0)
+
+ ENCODE(0); /* flags */
+ ENCODE(cnlen); /* cellname length */
+ if (copy_to_user(xdr, key->description + 4, cnlen) != 0)
+ goto fault;
+ if (cnlen & 3 &&
+ copy_to_user((u8 *)xdr + cnlen, &zero, 4 - (cnlen & 3)) != 0)
+ goto fault;
+ xdr += (cnlen + 3) >> 2;
+ ENCODE(ntoks); /* token count */
+
+ for (token = key->payload.data; token; token = token->next) {
+ toksize = 1 * 4; /* sec index */
+
+ switch (token->security_index) {
+ case RXRPC_SECURITY_RXKAD:
+ toksize += 8 * 4;
+ toksize += (token->kad->ticket_len + 3) & ~3;
+ ENCODE(toksize);
+ ENCODE(token->security_index);
+ ENCODE(token->kad->vice_id);
+ ENCODE(token->kad->kvno);
+ if (copy_to_user(xdr, token->kad->session_key, 8) != 0)
+ goto fault;
+ xdr += 8 >> 2;
+ ENCODE(token->kad->start);
+ ENCODE(token->kad->expiry);
+ ENCODE(token->kad->primary_flag);
+ tktlen = token->kad->ticket_len;
+ ENCODE(tktlen);
+ if (copy_to_user(xdr, token->kad->ticket, tktlen) != 0)
+ goto fault;
+ if (tktlen & 3 &&
+ copy_to_user((u8 *)xdr + tktlen, &zero,
+ 4 - (tktlen & 3)) != 0)
+ goto fault;
+ xdr += (tktlen + 3) >> 2;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+#undef ENCODE
+
+ ASSERTCMP((char __user *) xdr - buffer, ==, size);
+ _leave(" = %zu", size);
+ return size;
+
+fault:
+ _leave(" = -EFAULT");
+ return -EFAULT;
+}