The current cifs implementation discards a principal name found in the CIFS server's SecBlob. This patch adds the principal name into the data used for the request_key call. Combined with a separate cifs-utils patch, this enables cifs mounts using kerberos on servers that use different principal names than the default cifs/<hostname> or host/<hostname> which are tried by cifs.upcall. Signed-off-by: Martin Wilck <martin.wilck@xxxxxxxxxxxxxx> --- fs/cifs/asn1.c | 28 ++++++++++++++++++++++++---- fs/cifs/cifs_spnego.c | 10 ++++++++++ fs/cifs/cifs_spnego.h | 2 +- fs/cifs/cifsglob.h | 1 + fs/cifs/connect.c | 3 +++ 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/fs/cifs/asn1.c b/fs/cifs/asn1.c index cfd1ce3..5ab23f2 100644 --- a/fs/cifs/asn1.c +++ b/fs/cifs/asn1.c @@ -369,7 +369,7 @@ static unsigned char asn1_ulong_decode(struct asn1_ctx *ctx, *integer |= ch; } return 1; -} +} */ static unsigned char asn1_octets_decode(struct asn1_ctx *ctx, @@ -395,7 +395,7 @@ asn1_octets_decode(struct asn1_ctx *ctx, (*len)++; } return 1; -} */ +} static unsigned char asn1_subid_decode(struct asn1_ctx *ctx, unsigned long *subid) @@ -496,9 +496,9 @@ decode_negTokenInit(unsigned char *security_blob, int length, { struct asn1_ctx ctx; unsigned char *end; - unsigned char *sequence_end; + unsigned char *sequence_end, *principal; unsigned long *oid = NULL; - unsigned int cls, con, tag, oidlen, rc; + unsigned int cls, con, tag, oidlen, rc, princlen; /* cifs_dump_mem(" Received SecBlob ", security_blob, length); */ @@ -661,6 +661,26 @@ decode_negTokenInit(unsigned char *security_blob, int length, } cFYI(1, "Need to call asn1_octets_decode() function for %s", ctx.pointer); /* is this UTF-8 or ASCII? */ + if (asn1_octets_decode(&ctx, end, &principal, &princlen) == 0) { + cFYI(1, "Error decoding principal name exit10"); + return 0; + } else if (princlen == 0) { + cFYI(1, "Empty principal name"); + } else if (!strcmp(principal, "not_defined_in_RFC4178@please_ignore")) { + cFYI(1, "Ignoring principal name"); + } else { + server->principal = kmalloc(princlen+1, GFP_ATOMIC); + if (server->principal != NULL) { + memcpy(server->principal, principal, princlen); + server->principal[princlen] = '\0'; + cFYI(1, "Got principal: %s", server->principal); + } else { + kfree(principal); + cFYI(1, "Error allocating memory exit 11"); + return 0; + } + } + kfree(principal); decode_negtoken_exit: return 1; } diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c index 2272fd5..356a8a6 100644 --- a/fs/cifs/cifs_spnego.c +++ b/fs/cifs/cifs_spnego.c @@ -93,6 +93,9 @@ struct key_type cifs_spnego_key_type = { /* strlen of ";pid=0x" */ #define PID_KEY_LEN 7 +/* strlen of ";pri=" */ +#define PRINC_KEY_LEN 5 + /* get a key struct with a SPNEGO security blob, suitable for session setup */ struct key * cifs_get_spnego_key(struct cifs_ses *sesInfo) @@ -109,6 +112,8 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo) host=hostname sec=mechanism uid=0xFF user=username */ desc_len = MAX_VER_STR_LEN + HOST_KEY_LEN + strlen(hostname) + + (server->principal ? + PRINC_KEY_LEN + strlen(server->principal) : 0) + IP_KEY_LEN + INET6_ADDRSTRLEN + MAX_MECH_STR_LEN + UID_KEY_LEN + (sizeof(uid_t) * 2) + @@ -128,6 +133,11 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo) hostname); dp = description + strlen(description); + if (server->principal) { + sprintf(dp, "pri=%s;", server->principal); + dp = description + strlen(description); + } + /* add the server address */ if (server->dstaddr.ss_family == AF_INET) sprintf(dp, "ip4=%pI4", &sa->sin_addr); diff --git a/fs/cifs/cifs_spnego.h b/fs/cifs/cifs_spnego.h index 31bef9e..bae1877 100644 --- a/fs/cifs/cifs_spnego.h +++ b/fs/cifs/cifs_spnego.h @@ -23,7 +23,7 @@ #ifndef _CIFS_SPNEGO_H #define _CIFS_SPNEGO_H -#define CIFS_SPNEGO_UPCALL_VERSION 2 +#define CIFS_SPNEGO_UPCALL_VERSION 3 /* * The version field should always be set to CIFS_SPNEGO_UPCALL_VERSION. diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 95dad9d..86de4fb 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -238,6 +238,7 @@ struct TCP_Server_Info { char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; enum statusEnum tcpStatus; /* what we think the status is */ char *hostname; /* hostname portion of UNC string */ + char *principal; struct socket *ssocket; struct sockaddr_storage dstaddr; struct sockaddr_storage srcaddr; /* locally bind to this IP */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index f4af4cc..6fdca9f 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -618,6 +618,8 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server) } kfree(server->hostname); + if (server->principal != NULL) + kfree(server->principal); kfree(server); length = atomic_dec_return(&tcpSesAllocCount); @@ -1780,6 +1782,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) rc = PTR_ERR(tcp_ses->hostname); goto out_err_crypto_release; } + tcp_ses->principal = NULL; tcp_ses->noblocksnd = volume_info->noblocksnd; tcp_ses->noautotune = volume_info->noautotune; -- 1.7.6 -- To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html