2007-11-05 17:38:08 +03:00
/*
* fs / cifs / cifs_spnego . c - - SPNEGO upcall management for CIFS
*
* Copyright ( c ) 2007 Red Hat , Inc .
* Author ( s ) : Jeff Layton ( jlayton @ redhat . com )
*
* This library is free software ; you can redistribute it and / or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation ; either version 2.1 of the License , or
* ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See
* the GNU Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/list.h>
# include <linux/string.h>
# include <keys/user-type.h>
# include <linux/key-type.h>
# include "cifsglob.h"
# include "cifs_spnego.h"
# include "cifs_debug.h"
/* create a new cifs key */
static int
cifs_spnego_key_instantiate ( struct key * key , const void * data , size_t datalen )
{
char * payload ;
int ret ;
ret = - ENOMEM ;
payload = kmalloc ( datalen , GFP_KERNEL ) ;
if ( ! payload )
goto error ;
/* attach the data */
memcpy ( payload , data , datalen ) ;
rcu_assign_pointer ( key - > payload . data , payload ) ;
ret = 0 ;
error :
return ret ;
}
static void
cifs_spnego_key_destroy ( struct key * key )
{
kfree ( key - > payload . data ) ;
}
/*
* keytype for CIFS spnego keys
*/
struct key_type cifs_spnego_key_type = {
. name = " cifs.spnego " ,
. instantiate = cifs_spnego_key_instantiate ,
. match = user_match ,
. destroy = cifs_spnego_key_destroy ,
. describe = user_describe ,
} ;
2008-09-23 21:23:09 +04:00
/* length of longest version string e.g. strlen("ver=0xFF") */
# define MAX_VER_STR_LEN 8
/* length of longest security mechanism name, eg in future could have
* strlen ( " ;sec=ntlmsspi " ) */
# define MAX_MECH_STR_LEN 13
2008-11-14 06:35:10 +03:00
/* max possible addr len eg FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/128 */
# define MAX_IPV6_ADDR_LEN 43
2008-09-23 21:23:09 +04:00
/* strlen of "host=" */
# define HOST_KEY_LEN 5
/* strlen of ";ip4=" or ";ip6=" */
# define IP_KEY_LEN 5
/* strlen of ";uid=0x" */
# define UID_KEY_LEN 7
/* strlen of ";user=" */
# define USER_KEY_LEN 6
2007-11-05 17:38:08 +03:00
/* get a key struct with a SPNEGO security blob, suitable for session setup */
struct key *
2007-11-17 01:23:17 +03:00
cifs_get_spnego_key ( struct cifsSesInfo * sesInfo )
2007-11-05 17:38:08 +03:00
{
struct TCP_Server_Info * server = sesInfo - > server ;
char * description , * dp ;
size_t desc_len ;
struct key * spnego_key ;
2007-11-17 01:23:17 +03:00
const char * hostname = server - > hostname ;
2007-11-05 17:38:08 +03:00
2008-08-01 21:54:32 +04:00
/* length of fields (with semicolons): ver=0xyz ip4=ipaddress
host = hostname sec = mechanism uid = 0xFF user = username */
desc_len = MAX_VER_STR_LEN +
2008-09-23 21:23:09 +04:00
HOST_KEY_LEN + strlen ( hostname ) +
IP_KEY_LEN + MAX_IPV6_ADDR_LEN +
2008-08-01 21:54:32 +04:00
MAX_MECH_STR_LEN +
2008-09-23 21:23:09 +04:00
UID_KEY_LEN + ( sizeof ( uid_t ) * 2 ) +
USER_KEY_LEN + strlen ( sesInfo - > userName ) + 1 ;
2008-08-01 21:54:32 +04:00
2007-11-05 17:38:08 +03:00
spnego_key = ERR_PTR ( - ENOMEM ) ;
description = kzalloc ( desc_len , GFP_KERNEL ) ;
if ( description = = NULL )
goto out ;
dp = description ;
/* start with version and hostname portion of UNC string */
spnego_key = ERR_PTR ( - EINVAL ) ;
2007-11-16 21:32:52 +03:00
sprintf ( dp , " ver=0x%x;host=%s; " , CIFS_SPNEGO_UPCALL_VERSION ,
2007-11-05 17:38:08 +03:00
hostname ) ;
dp = description + strlen ( description ) ;
/* add the server address */
if ( server - > addr . sockAddr . sin_family = = AF_INET )
2008-10-31 10:56:28 +03:00
sprintf ( dp , " ip4=%pI4 " , & server - > addr . sockAddr . sin_addr ) ;
2007-11-05 17:38:08 +03:00
else if ( server - > addr . sockAddr . sin_family = = AF_INET6 )
2008-10-29 22:50:24 +03:00
sprintf ( dp , " ip6=%pi6 " , & server - > addr . sockAddr6 . sin6_addr ) ;
2007-11-05 17:38:08 +03:00
else
goto out ;
dp = description + strlen ( description ) ;
2008-08-19 23:35:33 +04:00
/* for now, only sec=krb5 and sec=mskrb5 are valid */
2007-11-05 17:38:08 +03:00
if ( server - > secType = = Kerberos )
sprintf ( dp , " ;sec=krb5 " ) ;
2008-08-19 23:35:33 +04:00
else if ( server - > secType = = MSKerberos )
sprintf ( dp , " ;sec=mskrb5 " ) ;
2007-11-05 17:38:08 +03:00
else
goto out ;
2007-11-08 19:13:31 +03:00
dp = description + strlen ( description ) ;
sprintf ( dp , " ;uid=0x%x " , sesInfo - > linux_uid ) ;
2008-04-02 17:33:47 +04:00
dp = description + strlen ( description ) ;
sprintf ( dp , " ;user=%s " , sesInfo - > userName ) ;
2007-11-05 17:38:08 +03:00
cFYI ( 1 , ( " key description = %s " , description ) ) ;
spnego_key = request_key ( & cifs_spnego_key_type , description , " " ) ;
2007-12-31 03:51:45 +03:00
# ifdef CONFIG_CIFS_DEBUG2
2007-11-05 17:38:08 +03:00
if ( cifsFYI & & ! IS_ERR ( spnego_key ) ) {
struct cifs_spnego_msg * msg = spnego_key - > payload . data ;
2008-02-05 18:51:24 +03:00
cifs_dump_mem ( " SPNEGO reply blob: " , msg - > data , min ( 1024U ,
2007-12-31 03:51:45 +03:00
msg - > secblob_len + msg - > sesskey_len ) ) ;
2007-11-05 17:38:08 +03:00
}
2007-12-31 03:51:45 +03:00
# endif /* CONFIG_CIFS_DEBUG2 */
2007-11-05 17:38:08 +03:00
out :
kfree ( description ) ;
return spnego_key ;
}