From patchwork Mon Sep 13 19:15:22 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shirish Pargaonkar X-Patchwork-Id: 175352 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id o8DJKNEq001999 for ; Mon, 13 Sep 2010 19:20:23 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754586Ab0IMTUA (ORCPT ); Mon, 13 Sep 2010 15:20:00 -0400 Received: from mail-vw0-f46.google.com ([209.85.212.46]:37725 "EHLO mail-vw0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754483Ab0IMTT7 (ORCPT ); Mon, 13 Sep 2010 15:19:59 -0400 Received: by vws3 with SMTP id 3so5033997vws.19 for ; Mon, 13 Sep 2010 12:19:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:from:to:cc:subject:date :message-id:x-mailer; bh=a6s1LEYmFIER4HbTsSq1zqVF/zquMXlvPUJGKg0eEIU=; b=smVplruy/enF6fZP7eAy5FPjS9DxmwvzCZYrqqSPx5LKe42daAvVmvjeMJ1PY+r0yF aOceXVfUVmVQU2KdmH6vmfMK+awKFlHugzJ28KYKEDv9jXgJ/evtGnnvW2NhdkdytnGM JXdzhH+C/U9pf7T5dQl77AeOmd+mahGIpsCb0= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:to:cc:subject:date:message-id:x-mailer; b=qzzxwPQZuSt4EbZkSt0pAkGT3fLraGQ8jVOZpUg8TeaJyVyZJUYwGcTQ2UpkZU9VJc rzFHiStIVGCsLWrexNjH9/S8ptX+YM+JiyG8YoAcmLJuG8UkSHn0laoIFgiXKBdTsQtE g3n+EzAr1LB9fBg2wR+4ReK0iQ+C9RJJyyaLk= Received: by 10.220.87.69 with SMTP id v5mr778710vcl.273.1284405598061; Mon, 13 Sep 2010 12:19:58 -0700 (PDT) Received: from localhost ([32.97.110.58]) by mx.google.com with ESMTPS id n9sm2427896vch.43.2010.09.13.12.19.56 (version=TLSv1/SSLv3 cipher=RC4-MD5); Mon, 13 Sep 2010 12:19:57 -0700 (PDT) From: shirishpargaonkar@gmail.com To: smfrench@gmail.com Cc: linux-cifs@vger.kernel.org, Shirish Pargaonkar Subject: [PATCH -v3 2/4 cifs] NTLMv2/NTLMSSP ntlmv2 within ntlmssp autentication code Date: Mon, 13 Sep 2010 14:15:22 -0500 Message-Id: <1284405322-31202-1-git-send-email-shirishpargaonkar@gmail.com> X-Mailer: git-send-email 1.6.0.2 Sender: linux-cifs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-cifs@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Mon, 13 Sep 2010 19:20:23 +0000 (UTC) diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index eed70ca..960c948 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -27,6 +27,7 @@ #include "md5.h" #include "cifs_unicode.h" #include "cifsproto.h" +#include "ntlmssp.h" #include #include @@ -262,6 +263,87 @@ void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, } #endif /* CIFS_WEAK_PW_HASH */ +/* This is just a filler for ntlmv2 type of security mechanisms. + * Older servers are not very particular about the contents of av pairs + * in the blob and for sec mechs like ntlmv2, there is no negotiation + * as in ntlmssp, so unless domain and server netbios and dns names + * are specified, there is no way to obtain name. In case of ntlmssp, + * server provides that info in type 2 challenge packet + */ +static int +build_avpair_blob(struct cifsSesInfo *ses) +{ + struct ntlmssp2_name *attrptr; + + ses->tilen = 2 * sizeof(struct ntlmssp2_name); + ses->tiblob = kzalloc(ses->tilen, GFP_KERNEL); + if (!ses->tiblob) { + ses->tilen = 0; + cERROR(1, "Challenge target info allocation failure"); + return -ENOMEM; + } + attrptr = (struct ntlmssp2_name *) ses->tiblob; + attrptr->type = cpu_to_le16(NTLMSSP_DOMAIN_TYPE); + + return 0; +} + +/* Server has provided av pairs/target info in the type 2 challenge + * packet and we have plucked it and stored within smb session. + * We parse that blob here to find netbios domain name to be used + * as part of ntlmv2 authentication (in Target String), if not already + * specified on the command line. + * If this function returns without any error but without fetching + * domain name, authentication may fail against some server but + * may not fail against other (those who are not very particular + * about target string i.e. for some, just user name might suffice. + */ +static int +find_domain_name(struct cifsSesInfo *ses) +{ + unsigned int attrsize; + unsigned int type; + unsigned char *blobptr; + unsigned char *blobend; + struct ntlmssp2_name *attrptr; + + if (!ses->tilen || !ses->tiblob) + return 0; + + if (ses->tilen < sizeof(struct ntlmssp2_name)) + return 0; + + blobend = ses->tiblob + ses->tilen; + blobptr = ses->tiblob; + attrptr = (struct ntlmssp2_name *) blobptr; + + while (blobptr <= blobend) { + type = le16_to_cpu(attrptr->type); + if (type == NTLMSSP_AV_EOL) + break; + blobptr += 2; /* advance attr type */ + attrsize = le16_to_cpu(attrptr->length); + blobptr += 2; /* advance attr size */ + if (type == NTLMSSP_AV_NB_DOMAIN_NAME) { + if (!ses->domainName) { + ses->domainName = + kmalloc(attrsize + 1, GFP_KERNEL); + if (!ses->domainName) + return -ENOMEM; + cifs_from_ucs2(ses->domainName, + (__le16 *)blobptr, + attrptr->length, + attrptr->length, + load_nls_default(), false); + } + } + blobptr += attrsize; /* advance attr value */ + attrptr = (struct ntlmssp2_name *) blobptr; + } + + return 0; +} + static int calc_ntlmv2_hash(struct cifsSesInfo *ses, const struct nls_table *nls_cp) { @@ -321,7 +403,8 @@ calc_exit_2: return rc; } -void setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf, +int +setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf, const struct nls_table *nls_cp) { int rc; @@ -333,15 +416,29 @@ void setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf, buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); get_random_bytes(&buf->client_chal, sizeof(buf->client_chal)); buf->reserved2 = 0; - buf->names[0].type = cpu_to_le16(NTLMSSP_DOMAIN_TYPE); - buf->names[0].length = 0; - buf->names[1].type = 0; - buf->names[1].length = 0; + + if (ses->server->secType == RawNTLMSSP) { + if (!ses->domainName) { + rc = find_domain_name(ses); + if (rc) { + cERROR(1, "error %d finding domain name", rc); + goto setup_ntlmv2_rsp_ret; + } + } + } else { + rc = build_avpair_blob(ses); + if (rc) { + cERROR(1, "error %d building av pair blob", rc); + return rc; + } + } /* calculate buf->ntlmv2_hash */ rc = calc_ntlmv2_hash(ses, nls_cp); - if (rc) + if (rc) { cERROR(1, "could not get v2 hash rc %d", rc); + goto setup_ntlmv2_rsp_ret; + } CalcNTLMv2_response(ses, resp_buf); /* now calculate the MAC key for NTLMv2 */ @@ -352,6 +449,14 @@ void setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf, memcpy(&ses->server->session_key.data.ntlmv2.resp, resp_buf, sizeof(struct ntlmv2_resp)); ses->server->session_key.len = 16 + sizeof(struct ntlmv2_resp); + + return 0; + +setup_ntlmv2_rsp_ret: + kfree(ses->tiblob); + ses->tilen = 0; + + return rc; } void CalcNTLMv2_response(const struct cifsSesInfo *ses, @@ -365,6 +470,10 @@ void CalcNTLMv2_response(const struct cifsSesInfo *ses, hmac_md5_update(v2_session_response+8, sizeof(struct ntlmv2_resp) - 8, &context); + if (ses->tilen) + hmac_md5_update(ses->tiblob, + ses->tilen, &context); + hmac_md5_final(v2_session_response, &context); /* cifs_dump_mem("v2_sess_rsp: ", v2_session_response, 32); */ } diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 14dfa9a..c68f31c 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -222,6 +222,8 @@ struct cifsSesInfo { char userName[MAX_USERNAME_SIZE + 1]; char *domainName; char *password; + unsigned int tilen; /* length of the target info blob */ + unsigned char *tiblob; /* target info blob in challenge response */ bool need_reconnect:1; /* connection reset, uid now invalid */ }; /* no more than one of the following three session flags may be set */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 14d036d..b0f4b56 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -663,7 +663,6 @@ struct ntlmv2_resp { __le64 time; __u64 client_chal; /* random */ __u32 reserved2; - struct ntlmssp2_name names[2]; /* array of name entries could follow ending in minimum 4 byte struct */ } __attribute__((packed)); diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 3f4fa81..c155479 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -367,7 +367,7 @@ extern int cifs_verify_signature(struct smb_hdr *, extern int cifs_calculate_session_key(struct session_key *key, const char *rn, const char *pass); extern void CalcNTLMv2_response(const struct cifsSesInfo *, char *); -extern void setup_ntlmv2_rsp(struct cifsSesInfo *, char *, +extern int setup_ntlmv2_rsp(struct cifsSesInfo *, char *, const struct nls_table *); #ifdef CONFIG_CIFS_WEAK_PW_HASH extern void calc_lanman_hash(const char *password, const char *cryptkey, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index c65c341..13c854e 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -603,13 +603,15 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) rc = 0; else rc = -EINVAL; - - if (server->sec_kerberos || server->sec_mskerberos) - server->secType = Kerberos; - else if (server->sec_ntlmssp) - server->secType = RawNTLMSSP; - else - rc = -EOPNOTSUPP; + if (server->secType == Kerberos) { + if (!server->sec_kerberos && + !server->sec_mskerberos) + rc = -EOPNOTSUPP; + } else if (server->secType == RawNTLMSSP) { + if (!server->sec_ntlmssp) + rc = -EOPNOTSUPP; + } else + rc = -EOPNOTSUPP; } } else server->capabilities &= ~CAP_EXTENDED_SECURITY; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 67dad54..f8891a2 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1740,6 +1740,8 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) if (ses == NULL) goto get_ses_fail; + ses->tilen = 0; + ses->tiblob = NULL; /* new SMB session uses our server ref */ ses->server = server; if (server->addr.sockAddr6.sin6_family == AF_INET6) diff --git a/fs/cifs/ntlmssp.h b/fs/cifs/ntlmssp.h index 49c9a4e..5d52e4a 100644 --- a/fs/cifs/ntlmssp.h +++ b/fs/cifs/ntlmssp.h @@ -61,6 +61,21 @@ #define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000 #define NTLMSSP_NEGOTIATE_56 0x80000000 +/* Define AV Pair Field IDs */ +enum av_field_type { + NTLMSSP_AV_EOL = 0, + NTLMSSP_AV_NB_COMPUTER_NAME, + NTLMSSP_AV_NB_DOMAIN_NAME, + NTLMSSP_AV_DNS_COMPUTER_NAME, + NTLMSSP_AV_DNS_DOMAIN_NAME, + NTLMSSP_AV_DNS_TREE_NAME, + NTLMSSP_AV_FLAGS, + NTLMSSP_AV_TIMESTAMP, + NTLMSSP_AV_RESTRICTION, + NTLMSSP_AV_TARGET_NAME, + NTLMSSP_AV_CHANNEL_BINDINGS +}; + /* Although typedefs are not commonly used for structure definitions */ /* in the Linux kernel, in this particular case they are useful */ /* to more closely match the standards document for NTLMSSP from */