114ae162cSJ. Bruce Fields #include <linux/types.h> 214ae162cSJ. Bruce Fields #include <linux/slab.h> 314ae162cSJ. Bruce Fields #include <linux/jiffies.h> 414ae162cSJ. Bruce Fields #include <linux/sunrpc/gss_krb5.h> 514ae162cSJ. Bruce Fields #include <linux/random.h> 614ae162cSJ. Bruce Fields #include <linux/pagemap.h> 714ae162cSJ. Bruce Fields #include <asm/scatterlist.h> 814ae162cSJ. Bruce Fields #include <linux/crypto.h> 914ae162cSJ. Bruce Fields 1014ae162cSJ. Bruce Fields #ifdef RPC_DEBUG 1114ae162cSJ. Bruce Fields # define RPCDBG_FACILITY RPCDBG_AUTH 1214ae162cSJ. Bruce Fields #endif 1314ae162cSJ. Bruce Fields 1414ae162cSJ. Bruce Fields static inline int 1514ae162cSJ. Bruce Fields gss_krb5_padding(int blocksize, int length) 1614ae162cSJ. Bruce Fields { 1714ae162cSJ. Bruce Fields /* Most of the code is block-size independent but currently we 1814ae162cSJ. Bruce Fields * use only 8: */ 1914ae162cSJ. Bruce Fields BUG_ON(blocksize != 8); 2014ae162cSJ. Bruce Fields return 8 - (length & 7); 2114ae162cSJ. Bruce Fields } 2214ae162cSJ. Bruce Fields 2314ae162cSJ. Bruce Fields static inline void 2414ae162cSJ. Bruce Fields gss_krb5_add_padding(struct xdr_buf *buf, int offset, int blocksize) 2514ae162cSJ. Bruce Fields { 2614ae162cSJ. Bruce Fields int padding = gss_krb5_padding(blocksize, buf->len - offset); 2714ae162cSJ. Bruce Fields char *p; 2814ae162cSJ. Bruce Fields struct kvec *iov; 2914ae162cSJ. Bruce Fields 3014ae162cSJ. Bruce Fields if (buf->page_len || buf->tail[0].iov_len) 3114ae162cSJ. Bruce Fields iov = &buf->tail[0]; 3214ae162cSJ. Bruce Fields else 3314ae162cSJ. Bruce Fields iov = &buf->head[0]; 3414ae162cSJ. Bruce Fields p = iov->iov_base + iov->iov_len; 3514ae162cSJ. Bruce Fields iov->iov_len += padding; 3614ae162cSJ. Bruce Fields buf->len += padding; 3714ae162cSJ. Bruce Fields memset(p, padding, padding); 3814ae162cSJ. Bruce Fields } 3914ae162cSJ. Bruce Fields 4014ae162cSJ. Bruce Fields static inline int 4114ae162cSJ. Bruce Fields gss_krb5_remove_padding(struct xdr_buf *buf, int blocksize) 4214ae162cSJ. Bruce Fields { 4314ae162cSJ. Bruce Fields u8 *ptr; 4414ae162cSJ. Bruce Fields u8 pad; 4567f97d83SChuck Lever size_t len = buf->len; 4614ae162cSJ. Bruce Fields 4714ae162cSJ. Bruce Fields if (len <= buf->head[0].iov_len) { 4814ae162cSJ. Bruce Fields pad = *(u8 *)(buf->head[0].iov_base + len - 1); 4914ae162cSJ. Bruce Fields if (pad > buf->head[0].iov_len) 5014ae162cSJ. Bruce Fields return -EINVAL; 5114ae162cSJ. Bruce Fields buf->head[0].iov_len -= pad; 5214ae162cSJ. Bruce Fields goto out; 5314ae162cSJ. Bruce Fields } else 5414ae162cSJ. Bruce Fields len -= buf->head[0].iov_len; 5514ae162cSJ. Bruce Fields if (len <= buf->page_len) { 5667f97d83SChuck Lever unsigned int last = (buf->page_base + len - 1) 5714ae162cSJ. Bruce Fields >>PAGE_CACHE_SHIFT; 5867f97d83SChuck Lever unsigned int offset = (buf->page_base + len - 1) 5914ae162cSJ. Bruce Fields & (PAGE_CACHE_SIZE - 1); 6087d918d6SJ. Bruce Fields ptr = kmap_atomic(buf->pages[last], KM_USER0); 6114ae162cSJ. Bruce Fields pad = *(ptr + offset); 6287d918d6SJ. Bruce Fields kunmap_atomic(ptr, KM_USER0); 6314ae162cSJ. Bruce Fields goto out; 6414ae162cSJ. Bruce Fields } else 6514ae162cSJ. Bruce Fields len -= buf->page_len; 6614ae162cSJ. Bruce Fields BUG_ON(len > buf->tail[0].iov_len); 6714ae162cSJ. Bruce Fields pad = *(u8 *)(buf->tail[0].iov_base + len - 1); 6814ae162cSJ. Bruce Fields out: 6914ae162cSJ. Bruce Fields /* XXX: NOTE: we do not adjust the page lengths--they represent 7014ae162cSJ. Bruce Fields * a range of data in the real filesystem page cache, and we need 7114ae162cSJ. Bruce Fields * to know that range so the xdr code can properly place read data. 7214ae162cSJ. Bruce Fields * However adjusting the head length, as we do above, is harmless. 7314ae162cSJ. Bruce Fields * In the case of a request that fits into a single page, the server 7414ae162cSJ. Bruce Fields * also uses length and head length together to determine the original 7514ae162cSJ. Bruce Fields * start of the request to copy the request for deferal; so it's 7614ae162cSJ. Bruce Fields * easier on the server if we adjust head and tail length in tandem. 7714ae162cSJ. Bruce Fields * It's not really a problem that we don't fool with the page and 7814ae162cSJ. Bruce Fields * tail lengths, though--at worst badly formed xdr might lead the 7914ae162cSJ. Bruce Fields * server to attempt to parse the padding. 8014ae162cSJ. Bruce Fields * XXX: Document all these weird requirements for gss mechanism 8114ae162cSJ. Bruce Fields * wrap/unwrap functions. */ 8214ae162cSJ. Bruce Fields if (pad > blocksize) 8314ae162cSJ. Bruce Fields return -EINVAL; 8414ae162cSJ. Bruce Fields if (buf->len > pad) 8514ae162cSJ. Bruce Fields buf->len -= pad; 8614ae162cSJ. Bruce Fields else 8714ae162cSJ. Bruce Fields return -EINVAL; 8814ae162cSJ. Bruce Fields return 0; 8914ae162cSJ. Bruce Fields } 9014ae162cSJ. Bruce Fields 9114ae162cSJ. Bruce Fields static inline void 9214ae162cSJ. Bruce Fields make_confounder(char *p, int blocksize) 9314ae162cSJ. Bruce Fields { 9414ae162cSJ. Bruce Fields static u64 i = 0; 9514ae162cSJ. Bruce Fields u64 *q = (u64 *)p; 9614ae162cSJ. Bruce Fields 9714ae162cSJ. Bruce Fields /* rfc1964 claims this should be "random". But all that's really 9814ae162cSJ. Bruce Fields * necessary is that it be unique. And not even that is necessary in 9914ae162cSJ. Bruce Fields * our case since our "gssapi" implementation exists only to support 10014ae162cSJ. Bruce Fields * rpcsec_gss, so we know that the only buffers we will ever encrypt 10114ae162cSJ. Bruce Fields * already begin with a unique sequence number. Just to hedge my bets 10214ae162cSJ. Bruce Fields * I'll make a half-hearted attempt at something unique, but ensuring 10314ae162cSJ. Bruce Fields * uniqueness would mean worrying about atomicity and rollover, and I 10414ae162cSJ. Bruce Fields * don't care enough. */ 10514ae162cSJ. Bruce Fields 10614ae162cSJ. Bruce Fields BUG_ON(blocksize != 8); 10714ae162cSJ. Bruce Fields *q = i++; 10814ae162cSJ. Bruce Fields } 10914ae162cSJ. Bruce Fields 11014ae162cSJ. Bruce Fields /* Assumptions: the head and tail of inbuf are ours to play with. 11114ae162cSJ. Bruce Fields * The pages, however, may be real pages in the page cache and we replace 11214ae162cSJ. Bruce Fields * them with scratch pages from **pages before writing to them. */ 11314ae162cSJ. Bruce Fields /* XXX: obviously the above should be documentation of wrap interface, 11414ae162cSJ. Bruce Fields * and shouldn't be in this kerberos-specific file. */ 11514ae162cSJ. Bruce Fields 11614ae162cSJ. Bruce Fields /* XXX factor out common code with seal/unseal. */ 11714ae162cSJ. Bruce Fields 11814ae162cSJ. Bruce Fields u32 11900fd6e14SJ. Bruce Fields gss_wrap_kerberos(struct gss_ctx *ctx, int offset, 12014ae162cSJ. Bruce Fields struct xdr_buf *buf, struct page **pages) 12114ae162cSJ. Bruce Fields { 12214ae162cSJ. Bruce Fields struct krb5_ctx *kctx = ctx->internal_ctx_id; 1239e57b302SJ. Bruce Fields char cksumdata[16]; 1249e57b302SJ. Bruce Fields struct xdr_netobj md5cksum = {.len = 0, .data = cksumdata}; 12514ae162cSJ. Bruce Fields int blocksize = 0, plainlen; 12614ae162cSJ. Bruce Fields unsigned char *ptr, *krb5_hdr, *msg_start; 12714ae162cSJ. Bruce Fields s32 now; 12814ae162cSJ. Bruce Fields int headlen; 12914ae162cSJ. Bruce Fields struct page **tmp_pages; 130eaa82edfSJ. Bruce Fields u32 seq_send; 13114ae162cSJ. Bruce Fields 13214ae162cSJ. Bruce Fields dprintk("RPC: gss_wrap_kerberos\n"); 13314ae162cSJ. Bruce Fields 13414ae162cSJ. Bruce Fields now = get_seconds(); 13514ae162cSJ. Bruce Fields 136378c6697SHerbert Xu blocksize = crypto_blkcipher_blocksize(kctx->enc); 13714ae162cSJ. Bruce Fields gss_krb5_add_padding(buf, offset, blocksize); 13814ae162cSJ. Bruce Fields BUG_ON((buf->len - offset) % blocksize); 13914ae162cSJ. Bruce Fields plainlen = blocksize + buf->len - offset; 14014ae162cSJ. Bruce Fields 14114ae162cSJ. Bruce Fields headlen = g_token_size(&kctx->mech_used, 22 + plainlen) - 14214ae162cSJ. Bruce Fields (buf->len - offset); 14314ae162cSJ. Bruce Fields 14414ae162cSJ. Bruce Fields ptr = buf->head[0].iov_base + offset; 14514ae162cSJ. Bruce Fields /* shift data to make room for header. */ 14614ae162cSJ. Bruce Fields /* XXX Would be cleverer to encrypt while copying. */ 14714ae162cSJ. Bruce Fields /* XXX bounds checking, slack, etc. */ 14814ae162cSJ. Bruce Fields memmove(ptr + headlen, ptr, buf->head[0].iov_len - offset); 14914ae162cSJ. Bruce Fields buf->head[0].iov_len += headlen; 15014ae162cSJ. Bruce Fields buf->len += headlen; 15114ae162cSJ. Bruce Fields BUG_ON((buf->len - offset - headlen) % blocksize); 15214ae162cSJ. Bruce Fields 15314ae162cSJ. Bruce Fields g_make_token_header(&kctx->mech_used, 22 + plainlen, &ptr); 15414ae162cSJ. Bruce Fields 15514ae162cSJ. Bruce Fields 15614ae162cSJ. Bruce Fields *ptr++ = (unsigned char) ((KG_TOK_WRAP_MSG>>8)&0xff); 15714ae162cSJ. Bruce Fields *ptr++ = (unsigned char) (KG_TOK_WRAP_MSG&0xff); 15814ae162cSJ. Bruce Fields 15914ae162cSJ. Bruce Fields /* ptr now at byte 2 of header described in rfc 1964, section 1.2.1: */ 16014ae162cSJ. Bruce Fields krb5_hdr = ptr - 2; 16114ae162cSJ. Bruce Fields msg_start = krb5_hdr + 24; 16214ae162cSJ. Bruce Fields 163e678e06bSJ. Bruce Fields *(__be16 *)(krb5_hdr + 2) = htons(SGN_ALG_DES_MAC_MD5); 16414ae162cSJ. Bruce Fields memset(krb5_hdr + 4, 0xff, 4); 165d922a84aSJ. Bruce Fields *(__be16 *)(krb5_hdr + 4) = htons(SEAL_ALG_DES); 16614ae162cSJ. Bruce Fields 16714ae162cSJ. Bruce Fields make_confounder(msg_start, blocksize); 16814ae162cSJ. Bruce Fields 16914ae162cSJ. Bruce Fields /* XXXJBF: UGH!: */ 17014ae162cSJ. Bruce Fields tmp_pages = buf->pages; 17114ae162cSJ. Bruce Fields buf->pages = pages; 172ca54f896SJ. Bruce Fields if (make_checksum("md5", krb5_hdr, 8, buf, 17314ae162cSJ. Bruce Fields offset + headlen - blocksize, &md5cksum)) 17439a21dd1SJ. Bruce Fields return GSS_S_FAILURE; 17514ae162cSJ. Bruce Fields buf->pages = tmp_pages; 17614ae162cSJ. Bruce Fields 17714ae162cSJ. Bruce Fields if (krb5_encrypt(kctx->seq, NULL, md5cksum.data, 17814ae162cSJ. Bruce Fields md5cksum.data, md5cksum.len)) 17939a21dd1SJ. Bruce Fields return GSS_S_FAILURE; 18014ae162cSJ. Bruce Fields memcpy(krb5_hdr + 16, 18114ae162cSJ. Bruce Fields md5cksum.data + md5cksum.len - KRB5_CKSUM_LENGTH, 18214ae162cSJ. Bruce Fields KRB5_CKSUM_LENGTH); 18314ae162cSJ. Bruce Fields 184eaa82edfSJ. Bruce Fields spin_lock(&krb5_seq_lock); 185eaa82edfSJ. Bruce Fields seq_send = kctx->seq_send++; 186eaa82edfSJ. Bruce Fields spin_unlock(&krb5_seq_lock); 187eaa82edfSJ. Bruce Fields 18814ae162cSJ. Bruce Fields /* XXX would probably be more efficient to compute checksum 18914ae162cSJ. Bruce Fields * and encrypt at the same time: */ 19014ae162cSJ. Bruce Fields if ((krb5_make_seq_num(kctx->seq, kctx->initiate ? 0 : 0xff, 191eaa82edfSJ. Bruce Fields seq_send, krb5_hdr + 16, krb5_hdr + 8))) 19239a21dd1SJ. Bruce Fields return GSS_S_FAILURE; 19314ae162cSJ. Bruce Fields 19414ae162cSJ. Bruce Fields if (gss_encrypt_xdr_buf(kctx->enc, buf, offset + headlen - blocksize, 19514ae162cSJ. Bruce Fields pages)) 19639a21dd1SJ. Bruce Fields return GSS_S_FAILURE; 19714ae162cSJ. Bruce Fields 19894efa934SJ. Bruce Fields return (kctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE; 19914ae162cSJ. Bruce Fields } 20014ae162cSJ. Bruce Fields 20114ae162cSJ. Bruce Fields u32 20200fd6e14SJ. Bruce Fields gss_unwrap_kerberos(struct gss_ctx *ctx, int offset, struct xdr_buf *buf) 20314ae162cSJ. Bruce Fields { 20414ae162cSJ. Bruce Fields struct krb5_ctx *kctx = ctx->internal_ctx_id; 20514ae162cSJ. Bruce Fields int signalg; 20614ae162cSJ. Bruce Fields int sealalg; 2079e57b302SJ. Bruce Fields char cksumdata[16]; 2089e57b302SJ. Bruce Fields struct xdr_netobj md5cksum = {.len = 0, .data = cksumdata}; 20914ae162cSJ. Bruce Fields s32 now; 21014ae162cSJ. Bruce Fields int direction; 21114ae162cSJ. Bruce Fields s32 seqnum; 21214ae162cSJ. Bruce Fields unsigned char *ptr; 21314ae162cSJ. Bruce Fields int bodysize; 21414ae162cSJ. Bruce Fields void *data_start, *orig_start; 21514ae162cSJ. Bruce Fields int data_len; 21614ae162cSJ. Bruce Fields int blocksize; 21714ae162cSJ. Bruce Fields 21814ae162cSJ. Bruce Fields dprintk("RPC: gss_unwrap_kerberos\n"); 21914ae162cSJ. Bruce Fields 22014ae162cSJ. Bruce Fields ptr = (u8 *)buf->head[0].iov_base + offset; 22114ae162cSJ. Bruce Fields if (g_verify_token_header(&kctx->mech_used, &bodysize, &ptr, 22214ae162cSJ. Bruce Fields buf->len - offset)) 22339a21dd1SJ. Bruce Fields return GSS_S_DEFECTIVE_TOKEN; 22414ae162cSJ. Bruce Fields 22514ae162cSJ. Bruce Fields if ((*ptr++ != ((KG_TOK_WRAP_MSG>>8)&0xff)) || 22614ae162cSJ. Bruce Fields (*ptr++ != (KG_TOK_WRAP_MSG &0xff)) ) 22739a21dd1SJ. Bruce Fields return GSS_S_DEFECTIVE_TOKEN; 22814ae162cSJ. Bruce Fields 22914ae162cSJ. Bruce Fields /* XXX sanity-check bodysize?? */ 23014ae162cSJ. Bruce Fields 23114ae162cSJ. Bruce Fields /* get the sign and seal algorithms */ 23214ae162cSJ. Bruce Fields 23314ae162cSJ. Bruce Fields signalg = ptr[0] + (ptr[1] << 8); 23494efa934SJ. Bruce Fields if (signalg != SGN_ALG_DES_MAC_MD5) 23539a21dd1SJ. Bruce Fields return GSS_S_DEFECTIVE_TOKEN; 23614ae162cSJ. Bruce Fields 23794efa934SJ. Bruce Fields sealalg = ptr[2] + (ptr[3] << 8); 238d922a84aSJ. Bruce Fields if (sealalg != SEAL_ALG_DES) 23939a21dd1SJ. Bruce Fields return GSS_S_DEFECTIVE_TOKEN; 24094efa934SJ. Bruce Fields 24194efa934SJ. Bruce Fields if ((ptr[4] != 0xff) || (ptr[5] != 0xff)) 24239a21dd1SJ. Bruce Fields return GSS_S_DEFECTIVE_TOKEN; 24314ae162cSJ. Bruce Fields 24414ae162cSJ. Bruce Fields if (gss_decrypt_xdr_buf(kctx->enc, buf, 24514ae162cSJ. Bruce Fields ptr + 22 - (unsigned char *)buf->head[0].iov_base)) 24639a21dd1SJ. Bruce Fields return GSS_S_DEFECTIVE_TOKEN; 24714ae162cSJ. Bruce Fields 24839a21dd1SJ. Bruce Fields if (make_checksum("md5", ptr - 2, 8, buf, 24939a21dd1SJ. Bruce Fields ptr + 22 - (unsigned char *)buf->head[0].iov_base, &md5cksum)) 25039a21dd1SJ. Bruce Fields return GSS_S_FAILURE; 25114ae162cSJ. Bruce Fields 25239a21dd1SJ. Bruce Fields if (krb5_encrypt(kctx->seq, NULL, md5cksum.data, 25339a21dd1SJ. Bruce Fields md5cksum.data, md5cksum.len)) 25439a21dd1SJ. Bruce Fields return GSS_S_FAILURE; 25514ae162cSJ. Bruce Fields 25639a21dd1SJ. Bruce Fields if (memcmp(md5cksum.data + 8, ptr + 14, 8)) 25739a21dd1SJ. Bruce Fields return GSS_S_BAD_SIG; 25814ae162cSJ. Bruce Fields 25914ae162cSJ. Bruce Fields /* it got through unscathed. Make sure the context is unexpired */ 26014ae162cSJ. Bruce Fields 26114ae162cSJ. Bruce Fields now = get_seconds(); 26214ae162cSJ. Bruce Fields 26314ae162cSJ. Bruce Fields if (now > kctx->endtime) 26439a21dd1SJ. Bruce Fields return GSS_S_CONTEXT_EXPIRED; 26514ae162cSJ. Bruce Fields 26614ae162cSJ. Bruce Fields /* do sequencing checks */ 26714ae162cSJ. Bruce Fields 26839a21dd1SJ. Bruce Fields if (krb5_get_seq_num(kctx->seq, ptr + 14, ptr + 6, &direction, 26939a21dd1SJ. Bruce Fields &seqnum)) 27039a21dd1SJ. Bruce Fields return GSS_S_BAD_SIG; 27114ae162cSJ. Bruce Fields 27214ae162cSJ. Bruce Fields if ((kctx->initiate && direction != 0xff) || 27314ae162cSJ. Bruce Fields (!kctx->initiate && direction != 0)) 27439a21dd1SJ. Bruce Fields return GSS_S_BAD_SIG; 27514ae162cSJ. Bruce Fields 27614ae162cSJ. Bruce Fields /* Copy the data back to the right position. XXX: Would probably be 27714ae162cSJ. Bruce Fields * better to copy and encrypt at the same time. */ 27814ae162cSJ. Bruce Fields 279378c6697SHerbert Xu blocksize = crypto_blkcipher_blocksize(kctx->enc); 28014ae162cSJ. Bruce Fields data_start = ptr + 22 + blocksize; 28114ae162cSJ. Bruce Fields orig_start = buf->head[0].iov_base + offset; 28214ae162cSJ. Bruce Fields data_len = (buf->head[0].iov_base + buf->head[0].iov_len) - data_start; 28314ae162cSJ. Bruce Fields memmove(orig_start, data_start, data_len); 28414ae162cSJ. Bruce Fields buf->head[0].iov_len -= (data_start - orig_start); 28514ae162cSJ. Bruce Fields buf->len -= (data_start - orig_start); 28614ae162cSJ. Bruce Fields 28714ae162cSJ. Bruce Fields if (gss_krb5_remove_padding(buf, blocksize)) 28839a21dd1SJ. Bruce Fields return GSS_S_DEFECTIVE_TOKEN; 28914ae162cSJ. Bruce Fields 29039a21dd1SJ. Bruce Fields return GSS_S_COMPLETE; 29114ae162cSJ. Bruce Fields } 292