2007-12-05 18:23:39 +03:00
/*
2008-07-16 17:27:05 -07:00
* CIFS user - space helper .
2007-12-05 18:23:39 +03:00
* Copyright ( C ) Igor Mammedov ( niallain @ gmail . com ) 2007
2009-07-09 21:04:08 -04:00
* Copyright ( C ) Jeff Layton ( jlayton @ redhat . com ) 2009
2007-12-05 18:23:39 +03:00
*
* Used by / sbin / request - key for handling
2007-12-19 17:48:43 +03:00
* cifs upcall for kerberos authorization of access to share and
* cifs upcall for DFS srver name resolving ( IPv4 / IPv6 aware ) .
2008-08-05 13:27:07 -05:00
* You should have keyutils installed and add something like the
* following lines to / etc / request - key . conf file :
2007-12-05 18:23:39 +03:00
2008-08-05 13:27:07 -05:00
create cifs . spnego * * / usr / local / sbin / cifs . upcall % k
create dns_resolver * * / usr / local / sbin / cifs . upcall % k
2007-12-05 18:23:39 +03:00
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
* This program 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 General Public License for more details .
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include "includes.h"
2009-09-17 08:52:22 +02:00
# include "../libcli/auth/spnego.h"
2009-11-27 15:52:57 +01:00
# include "smb_krb5.h"
2007-12-05 18:23:39 +03:00
# include <keyutils.h>
2009-08-26 06:15:42 -04:00
# include <getopt.h>
2007-12-05 18:23:39 +03:00
# include "cifs_spnego.h"
2009-09-04 06:29:44 -04:00
# define CIFS_DEFAULT_KRB5_DIR " / tmp"
# define CIFS_DEFAULT_KRB5_PREFIX "krb5cc_"
# define MAX_CCNAME_LEN PATH_MAX + 5
2009-08-14 07:59:50 -04:00
const char * CIFSSPNEGO_VERSION = " 1.3 " ;
2008-07-16 17:27:05 -07:00
static const char * prog = " cifs.upcall " ;
2009-08-14 07:59:49 -04:00
typedef enum _sectype {
2008-09-12 21:13:54 -04:00
NONE = 0 ,
2007-12-05 18:23:39 +03:00
KRB5 ,
MS_KRB5
2009-08-14 07:59:49 -04:00
} sectype_t ;
2007-12-05 18:23:39 +03:00
2009-09-04 06:29:44 -04:00
/* does the ccache have a valid TGT? */
static time_t
get_tgt_time ( const char * ccname ) {
krb5_context context ;
krb5_ccache ccache ;
krb5_cc_cursor cur ;
krb5_creds creds ;
krb5_principal principal ;
time_t credtime = 0 ;
2009-11-12 00:52:38 +01:00
char * realm = NULL ;
2009-09-04 06:29:44 -04:00
if ( krb5_init_context ( & context ) ) {
syslog ( LOG_DEBUG , " %s: unable to init krb5 context " , __func__ ) ;
return 0 ;
2009-08-14 07:59:49 -04:00
}
2009-07-09 21:04:08 -04:00
2009-09-04 06:29:44 -04:00
if ( krb5_cc_resolve ( context , ccname , & ccache ) ) {
syslog ( LOG_DEBUG , " %s: unable to resolve krb5 cache " , __func__ ) ;
goto err_cache ;
}
if ( krb5_cc_set_flags ( context , ccache , 0 ) ) {
syslog ( LOG_DEBUG , " %s: unable to set flags " , __func__ ) ;
goto err_cache ;
}
if ( krb5_cc_get_principal ( context , ccache , & principal ) ) {
syslog ( LOG_DEBUG , " %s: unable to get principal " , __func__ ) ;
goto err_princ ;
}
if ( krb5_cc_start_seq_get ( context , ccache , & cur ) ) {
syslog ( LOG_DEBUG , " %s: unable to seq start " , __func__ ) ;
goto err_ccstart ;
}
2009-11-12 00:52:38 +01:00
if ( ( realm = smb_krb5_principal_get_realm ( context , principal ) ) = = NULL ) {
syslog ( LOG_DEBUG , " %s: unable to get realm " , __func__ ) ;
goto err_ccstart ;
}
2009-09-04 06:29:44 -04:00
while ( ! credtime & & ! krb5_cc_next_cred ( context , ccache , & cur , & creds ) ) {
2009-11-12 00:52:38 +01:00
char * name ;
if ( smb_krb5_unparse_name ( NULL , context , creds . server , & name ) ) {
syslog ( LOG_DEBUG , " %s: unable to unparse name " , __func__ ) ;
goto err_endseq ;
}
if ( krb5_realm_compare ( context , creds . server , principal ) & &
strnequal ( name , KRB5_TGS_NAME , KRB5_TGS_NAME_SIZE ) & &
strnequal ( name + KRB5_TGS_NAME_SIZE + 1 , realm , strlen ( realm ) ) & &
2009-09-04 06:29:44 -04:00
creds . times . endtime > time ( NULL ) )
credtime = creds . times . endtime ;
krb5_free_cred_contents ( context , & creds ) ;
2009-11-12 00:52:38 +01:00
TALLOC_FREE ( name ) ;
2009-09-04 06:29:44 -04:00
}
2009-11-12 00:52:38 +01:00
err_endseq :
2009-09-04 06:29:44 -04:00
krb5_cc_end_seq_get ( context , ccache , & cur ) ;
err_ccstart :
krb5_free_principal ( context , principal ) ;
err_princ :
2009-11-25 15:06:19 +01:00
# if defined(KRB5_TC_OPENCLOSE)
2009-09-04 06:29:44 -04:00
krb5_cc_set_flags ( context , ccache , KRB5_TC_OPENCLOSE ) ;
2009-11-25 15:06:19 +01:00
# endif
2009-09-04 06:29:44 -04:00
krb5_cc_close ( context , ccache ) ;
err_cache :
krb5_free_context ( context ) ;
return credtime ;
}
static int
krb5cc_filter ( const struct dirent * dirent )
{
if ( strstr ( dirent - > d_name , CIFS_DEFAULT_KRB5_PREFIX ) )
return 1 ;
else
return 0 ;
}
/* search for a credcache that looks like a likely candidate */
static char *
find_krb5_cc ( const char * dirname , uid_t uid )
{
struct dirent * * namelist ;
struct stat sbuf ;
char ccname [ MAX_CCNAME_LEN ] , * credpath , * best_cache = NULL ;
int i , n ;
time_t cred_time , best_time = 0 ;
n = scandir ( dirname , & namelist , krb5cc_filter , NULL ) ;
if ( n < 0 ) {
syslog ( LOG_DEBUG , " %s: scandir error on directory '%s': %s " ,
__func__ , dirname , strerror ( errno ) ) ;
2009-07-09 21:04:08 -04:00
return NULL ;
2009-08-14 07:59:49 -04:00
}
2009-07-09 21:04:08 -04:00
2009-09-04 06:29:44 -04:00
for ( i = 0 ; i < n ; i + + ) {
snprintf ( ccname , sizeof ( ccname ) , " FILE:%s/%s " , dirname ,
namelist [ i ] - > d_name ) ;
credpath = ccname + 5 ;
syslog ( LOG_DEBUG , " %s: considering %s " , __func__ , credpath ) ;
if ( lstat ( credpath , & sbuf ) ) {
syslog ( LOG_DEBUG , " %s: stat error on '%s': %s " ,
__func__ , credpath , strerror ( errno ) ) ;
free ( namelist [ i ] ) ;
continue ;
}
if ( sbuf . st_uid ! = uid ) {
syslog ( LOG_DEBUG , " %s: %s is owned by %u, not %u " ,
__func__ , credpath , sbuf . st_uid , uid ) ;
free ( namelist [ i ] ) ;
continue ;
}
if ( ! S_ISREG ( sbuf . st_mode ) ) {
syslog ( LOG_DEBUG , " %s: %s is not a regular file " ,
__func__ , credpath ) ;
free ( namelist [ i ] ) ;
continue ;
}
if ( ! ( cred_time = get_tgt_time ( ccname ) ) ) {
syslog ( LOG_DEBUG , " %s: %s is not a valid credcache. " ,
__func__ , ccname ) ;
free ( namelist [ i ] ) ;
continue ;
}
2009-07-09 21:04:08 -04:00
2009-09-04 06:29:44 -04:00
if ( cred_time < = best_time ) {
syslog ( LOG_DEBUG , " %s: %s expires sooner than current "
" best. " , __func__ , ccname ) ;
free ( namelist [ i ] ) ;
2009-07-09 21:04:08 -04:00
continue ;
}
2009-09-04 06:29:44 -04:00
syslog ( LOG_DEBUG , " %s: %s is valid ccache " , __func__ , ccname ) ;
free ( best_cache ) ;
best_cache = SMB_STRNDUP ( ccname , MAX_CCNAME_LEN ) ;
best_time = cred_time ;
free ( namelist [ i ] ) ;
2009-07-09 21:04:08 -04:00
}
2009-09-04 06:29:44 -04:00
free ( namelist ) ;
return best_cache ;
2009-07-09 21:04:08 -04:00
}
2007-12-05 18:23:39 +03:00
/*
* Prepares AP - REQ data for mechToken and gets session key
* Uses credentials from cache . It will not ask for password
* you should receive credentials for yuor name manually using
* kinit or whatever you wish .
*
* in :
* oid - string with OID / Could be OID_KERBEROS5
* or OID_KERBEROS5_OLD
* principal - Service name .
* Could be " cifs/FQDN " for KRB5 OID
* or for MS_KRB5 OID style server principal
* like " pdc$@YOUR.REALM.NAME "
*
* out :
* secblob - pointer for spnego wrapped AP - REQ data to be stored
* sess_key - pointer for SessionKey data to be stored
*
* ret : 0 - success , others - failure
2009-08-14 07:59:49 -04:00
*/
2008-09-12 21:13:54 -04:00
static int
2009-07-09 21:04:08 -04:00
handle_krb5_mech ( const char * oid , const char * principal , DATA_BLOB * secblob ,
DATA_BLOB * sess_key , const char * ccname )
2007-12-05 18:23:39 +03:00
{
int retval ;
DATA_BLOB tkt , tkt_wrapped ;
2009-08-14 07:59:49 -04:00
syslog ( LOG_DEBUG , " %s: getting service ticket for %s " , __func__ ,
principal ) ;
2007-12-05 18:23:39 +03:00
/* get a kerberos ticket for the service and extract the session key */
2009-07-09 21:04:08 -04:00
retval = cli_krb5_get_ticket ( principal , 0 , & tkt , sess_key , 0 , ccname ,
2008-10-13 17:29:22 +02:00
NULL , NULL ) ;
2007-12-05 18:23:39 +03:00
2009-08-14 07:59:49 -04:00
if ( retval ) {
syslog ( LOG_DEBUG , " %s: failed to obtain service ticket (%d) " ,
__func__ , retval ) ;
2007-12-05 18:23:39 +03:00
return retval ;
2009-08-14 07:59:49 -04:00
}
syslog ( LOG_DEBUG , " %s: obtained service ticket " , __func__ ) ;
2007-12-05 18:23:39 +03:00
/* wrap that up in a nice GSS-API wrapping */
tkt_wrapped = spnego_gen_krb5_wrap ( tkt , TOK_ID_KRB_AP_REQ ) ;
/* and wrap that in a shiny SPNEGO wrapper */
2008-08-19 21:29:41 -04:00
* secblob = gen_negTokenInit ( oid , tkt_wrapped ) ;
2007-12-05 18:23:39 +03:00
data_blob_free ( & tkt_wrapped ) ;
data_blob_free ( & tkt ) ;
return retval ;
}
2009-08-14 07:59:49 -04:00
# define DKD_HAVE_HOSTNAME 0x1
# define DKD_HAVE_VERSION 0x2
# define DKD_HAVE_SEC 0x4
2009-08-14 07:59:50 -04:00
# define DKD_HAVE_IP 0x8
# define DKD_HAVE_UID 0x10
# define DKD_HAVE_PID 0x20
2009-08-26 06:26:02 -04:00
# define DKD_MUSTHAVE_SET (DKD_HAVE_HOSTNAME|DKD_HAVE_VERSION|DKD_HAVE_SEC)
2007-12-05 18:23:39 +03:00
2009-08-26 06:26:02 -04:00
struct decoded_args {
2009-08-14 07:59:49 -04:00
int ver ;
char * hostname ;
2009-08-14 07:59:50 -04:00
char * ip ;
2009-08-14 07:59:49 -04:00
uid_t uid ;
pid_t pid ;
sectype_t sec ;
} ;
2009-08-14 07:59:50 -04:00
static unsigned int
2009-08-14 07:59:49 -04:00
decode_key_description ( const char * desc , struct decoded_args * arg )
2007-12-05 18:23:39 +03:00
{
2009-08-14 07:59:50 -04:00
int len ;
2007-12-05 18:23:39 +03:00
int retval = 0 ;
char * pos ;
const char * tkn = desc ;
do {
pos = index ( tkn , ' ; ' ) ;
if ( strncmp ( tkn , " host= " , 5 ) = = 0 ) {
2009-08-14 07:59:49 -04:00
if ( pos = = NULL )
2007-12-05 18:23:39 +03:00
len = strlen ( tkn ) ;
2009-08-14 07:59:49 -04:00
else
2007-12-05 18:23:39 +03:00
len = pos - tkn ;
2009-08-14 07:59:49 -04:00
2007-12-05 18:23:39 +03:00
len - = 4 ;
2009-08-14 07:59:49 -04:00
SAFE_FREE ( arg - > hostname ) ;
arg - > hostname = SMB_XMALLOC_ARRAY ( char , len ) ;
strlcpy ( arg - > hostname , tkn + 5 , len ) ;
2007-12-05 18:23:39 +03:00
retval | = DKD_HAVE_HOSTNAME ;
2009-08-14 07:59:50 -04:00
} else if ( ! strncmp ( tkn , " ip4= " , 4 ) | |
! strncmp ( tkn , " ip6= " , 4 ) ) {
if ( pos = = NULL )
len = strlen ( tkn ) ;
else
len = pos - tkn ;
len - = 3 ;
SAFE_FREE ( arg - > ip ) ;
arg - > ip = SMB_XMALLOC_ARRAY ( char , len ) ;
strlcpy ( arg - > ip , tkn + 4 , len ) ;
retval | = DKD_HAVE_IP ;
2009-07-09 21:04:08 -04:00
} else if ( strncmp ( tkn , " pid= " , 4 ) = = 0 ) {
errno = 0 ;
2009-08-14 07:59:49 -04:00
arg - > pid = strtol ( tkn + 4 , NULL , 0 ) ;
2009-07-09 21:04:08 -04:00
if ( errno ! = 0 ) {
2009-08-14 07:59:49 -04:00
syslog ( LOG_ERR , " Invalid pid format: %s " ,
2009-07-09 21:04:08 -04:00
strerror ( errno ) ) ;
return 1 ;
} else {
retval | = DKD_HAVE_PID ;
}
2007-12-05 18:23:39 +03:00
} else if ( strncmp ( tkn , " sec= " , 4 ) = = 0 ) {
if ( strncmp ( tkn + 4 , " krb5 " , 4 ) = = 0 ) {
retval | = DKD_HAVE_SEC ;
2009-08-14 07:59:49 -04:00
arg - > sec = KRB5 ;
2008-08-19 21:29:41 -04:00
} else if ( strncmp ( tkn + 4 , " mskrb5 " , 6 ) = = 0 ) {
retval | = DKD_HAVE_SEC ;
2009-08-14 07:59:49 -04:00
arg - > sec = MS_KRB5 ;
2007-12-05 18:23:39 +03:00
}
} else if ( strncmp ( tkn , " uid= " , 4 ) = = 0 ) {
errno = 0 ;
2009-08-14 07:59:49 -04:00
arg - > uid = strtol ( tkn + 4 , NULL , 16 ) ;
2007-12-05 18:23:39 +03:00
if ( errno ! = 0 ) {
2009-08-14 07:59:49 -04:00
syslog ( LOG_ERR , " Invalid uid format: %s " ,
2007-12-05 18:23:39 +03:00
strerror ( errno ) ) ;
return 1 ;
} else {
retval | = DKD_HAVE_UID ;
}
} else if ( strncmp ( tkn , " ver= " , 4 ) = = 0 ) { /* if version */
errno = 0 ;
2009-08-14 07:59:49 -04:00
arg - > ver = strtol ( tkn + 4 , NULL , 16 ) ;
2007-12-05 18:23:39 +03:00
if ( errno ! = 0 ) {
2009-08-14 07:59:49 -04:00
syslog ( LOG_ERR , " Invalid version format: %s " ,
2007-12-05 18:23:39 +03:00
strerror ( errno ) ) ;
return 1 ;
} else {
retval | = DKD_HAVE_VERSION ;
}
}
if ( pos = = NULL )
break ;
tkn = pos + 1 ;
} while ( tkn ) ;
return retval ;
}
2008-09-12 21:13:54 -04:00
static int
cifs_resolver ( const key_serial_t key , const char * key_descr )
2007-12-19 17:48:43 +03:00
{
int c ;
struct addrinfo * addr ;
char ip [ INET6_ADDRSTRLEN ] ;
void * p ;
const char * keyend = key_descr ;
/* skip next 4 ';' delimiters to get to description */
for ( c = 1 ; c < = 4 ; c + + ) {
keyend = index ( keyend + 1 , ' ; ' ) ;
if ( ! keyend ) {
2009-08-14 07:59:49 -04:00
syslog ( LOG_ERR , " invalid key description: %s " ,
2007-12-19 17:48:43 +03:00
key_descr ) ;
return 1 ;
}
}
keyend + + ;
/* resolve name to ip */
c = getaddrinfo ( keyend , NULL , NULL , & addr ) ;
if ( c ) {
2009-08-14 07:59:49 -04:00
syslog ( LOG_ERR , " unable to resolve hostname: %s [%s] " ,
2007-12-19 17:48:43 +03:00
keyend , gai_strerror ( c ) ) ;
return 1 ;
}
/* conver ip to string form */
2009-08-14 07:59:49 -04:00
if ( addr - > ai_family = = AF_INET )
2007-12-19 17:48:43 +03:00
p = & ( ( ( struct sockaddr_in * ) addr - > ai_addr ) - > sin_addr ) ;
2009-08-14 07:59:49 -04:00
else
2007-12-19 17:48:43 +03:00
p = & ( ( ( struct sockaddr_in6 * ) addr - > ai_addr ) - > sin6_addr ) ;
2009-08-14 07:59:49 -04:00
2007-12-19 17:48:43 +03:00
if ( ! inet_ntop ( addr - > ai_family , p , ip , sizeof ( ip ) ) ) {
2009-08-14 07:59:49 -04:00
syslog ( LOG_ERR , " %s: inet_ntop: %s " , __func__ , strerror ( errno ) ) ;
2007-12-19 17:48:43 +03:00
freeaddrinfo ( addr ) ;
return 1 ;
}
/* setup key */
c = keyctl_instantiate ( key , ip , strlen ( ip ) + 1 , 0 ) ;
if ( c = = - 1 ) {
2009-08-14 07:59:49 -04:00
syslog ( LOG_ERR , " %s: keyctl_instantiate: %s " , __func__ ,
strerror ( errno ) ) ;
2007-12-19 17:48:43 +03:00
freeaddrinfo ( addr ) ;
return 1 ;
}
freeaddrinfo ( addr ) ;
return 0 ;
}
2009-08-14 07:59:51 -04:00
/*
* Older kernels sent IPv6 addresses without colons . Well , at least
* they ' re fixed - length strings . Convert these addresses to have colon
* delimiters to make getaddrinfo happy .
*/
static void
convert_inet6_addr ( const char * from , char * to )
{
int i = 1 ;
while ( * from ) {
* to + + = * from + + ;
if ( ! ( i + + % 4 ) & & * from )
* to + + = ' : ' ;
}
* to = 0 ;
}
2009-08-14 07:59:50 -04:00
static int
2009-08-14 07:59:51 -04:00
ip_to_fqdn ( const char * addrstr , char * host , size_t hostlen )
2009-08-14 07:59:50 -04:00
{
int rc ;
struct addrinfo hints = { . ai_flags = AI_NUMERICHOST } ;
struct addrinfo * res ;
2009-08-14 07:59:51 -04:00
const char * ipaddr = addrstr ;
char converted [ INET6_ADDRSTRLEN + 1 ] ;
if ( ( strlen ( ipaddr ) > INET_ADDRSTRLEN ) & & ! strchr ( ipaddr , ' : ' ) ) {
convert_inet6_addr ( ipaddr , converted ) ;
ipaddr = converted ;
}
2009-08-14 07:59:50 -04:00
rc = getaddrinfo ( ipaddr , NULL , & hints , & res ) ;
if ( rc ) {
2009-08-14 07:59:51 -04:00
syslog ( LOG_DEBUG , " %s: failed to resolve %s to "
" ipaddr: %s " , __func__ , ipaddr ,
rc = = EAI_SYSTEM ? strerror ( errno ) : gai_strerror ( rc ) ) ;
2009-08-14 07:59:50 -04:00
return rc ;
}
rc = getnameinfo ( res - > ai_addr , res - > ai_addrlen , host , hostlen ,
NULL , 0 , NI_NAMEREQD ) ;
freeaddrinfo ( res ) ;
if ( rc ) {
syslog ( LOG_DEBUG , " %s: failed to resolve %s to fqdn: %s " ,
__func__ , ipaddr ,
rc = = EAI_SYSTEM ? strerror ( errno ) : gai_strerror ( rc ) ) ;
return rc ;
}
syslog ( LOG_DEBUG , " %s: resolved %s to %s " , __func__ , ipaddr , host ) ;
return 0 ;
}
2008-09-12 21:13:54 -04:00
static void
2008-08-05 15:36:11 -05:00
usage ( void )
2008-07-16 17:27:05 -07:00
{
2009-08-26 06:26:02 -04:00
syslog ( LOG_INFO , " Usage: %s [-t] [-v] key_serial " , prog ) ;
fprintf ( stderr , " Usage: %s [-t] [-v] key_serial \n " , prog ) ;
2008-07-16 17:27:05 -07:00
}
2009-08-26 06:15:42 -04:00
const struct option long_options [ ] = {
2009-08-26 06:26:02 -04:00
{ " trust-dns " , 0 , NULL , ' t ' } ,
2009-08-26 06:15:42 -04:00
{ " version " , 0 , NULL , ' v ' } ,
{ NULL , 0 , NULL , 0 }
} ;
2007-12-05 18:23:39 +03:00
int main ( const int argc , char * const argv [ ] )
{
2007-12-05 17:54:09 -05:00
struct cifs_spnego_msg * keydata = NULL ;
2007-12-05 18:23:39 +03:00
DATA_BLOB secblob = data_blob_null ;
DATA_BLOB sess_key = data_blob_null ;
2008-08-12 14:32:54 -04:00
key_serial_t key = 0 ;
2007-12-05 18:23:39 +03:00
size_t datalen ;
2009-08-14 07:59:50 -04:00
unsigned int have ;
2007-12-05 18:23:39 +03:00
long rc = 1 ;
2009-08-26 06:26:02 -04:00
int c , try_dns = 0 ;
char * buf , * princ = NULL , * ccname = NULL ;
char hostbuf [ NI_MAXHOST ] , * host ;
2009-08-14 07:59:49 -04:00
struct decoded_args arg = { } ;
2008-08-21 21:21:48 -04:00
const char * oid ;
2007-12-05 18:23:39 +03:00
2009-08-26 06:26:02 -04:00
hostbuf [ 0 ] = ' \0 ' ;
2007-12-05 18:23:39 +03:00
openlog ( prog , 0 , LOG_DAEMON ) ;
2009-08-26 06:26:02 -04:00
while ( ( c = getopt_long ( argc , argv , " ctv " , long_options , NULL ) ) ! = - 1 ) {
2007-12-05 18:23:39 +03:00
switch ( c ) {
2009-08-14 07:59:49 -04:00
case ' c ' :
2009-08-14 07:59:50 -04:00
/* legacy option -- skip it */
2007-12-05 18:23:39 +03:00
break ;
2009-08-26 06:26:02 -04:00
case ' t ' :
try_dns + + ;
break ;
2009-08-14 07:59:49 -04:00
case ' v ' :
2008-07-16 17:27:05 -07:00
printf ( " version: %s \n " , CIFSSPNEGO_VERSION ) ;
goto out ;
2009-08-14 07:59:49 -04:00
default :
2009-08-14 07:59:49 -04:00
syslog ( LOG_ERR , " unknown option: %c " , c ) ;
2007-12-05 18:23:39 +03:00
goto out ;
}
}
2008-07-16 17:27:05 -07:00
/* is there a key? */
if ( argc < = optind ) {
2008-07-23 14:25:17 -05:00
usage ( ) ;
2008-07-16 17:27:05 -07:00
goto out ;
}
2007-12-05 18:23:39 +03:00
/* get key and keyring values */
errno = 0 ;
key = strtol ( argv [ optind ] , NULL , 10 ) ;
if ( errno ! = 0 ) {
2008-08-12 14:32:54 -04:00
key = 0 ;
2009-08-14 07:59:49 -04:00
syslog ( LOG_ERR , " Invalid key format: %s " , strerror ( errno ) ) ;
2007-12-05 18:23:39 +03:00
goto out ;
}
rc = keyctl_describe_alloc ( key , & buf ) ;
if ( rc = = - 1 ) {
2009-08-14 07:59:49 -04:00
syslog ( LOG_ERR , " keyctl_describe_alloc failed: %s " ,
2007-12-05 18:23:39 +03:00
strerror ( errno ) ) ;
rc = 1 ;
goto out ;
}
2009-08-14 07:59:49 -04:00
syslog ( LOG_DEBUG , " key description: %s " , buf ) ;
2008-07-24 09:32:53 -05:00
if ( ( strncmp ( buf , " cifs.resolver " , sizeof ( " cifs.resolver " ) - 1 ) = = 0 ) | |
( strncmp ( buf , " dns_resolver " , sizeof ( " dns_resolver " ) - 1 ) = = 0 ) ) {
2007-12-19 17:48:43 +03:00
rc = cifs_resolver ( key , buf ) ;
goto out ;
}
2009-08-14 07:59:50 -04:00
have = decode_key_description ( buf , & arg ) ;
SAFE_FREE ( buf ) ;
if ( ( have & DKD_MUSTHAVE_SET ) ! = DKD_MUSTHAVE_SET ) {
2009-08-14 07:59:49 -04:00
syslog ( LOG_ERR , " unable to get necessary params from key "
2009-08-14 07:59:50 -04:00
" description (0x%x) " , have ) ;
2007-12-05 18:23:39 +03:00
rc = 1 ;
goto out ;
}
2009-08-14 07:59:49 -04:00
if ( arg . ver > CIFS_SPNEGO_UPCALL_VERSION ) {
2009-08-14 07:59:49 -04:00
syslog ( LOG_ERR , " incompatible kernel upcall version: 0x%x " ,
2009-08-14 07:59:49 -04:00
arg . ver ) ;
2007-12-05 18:23:39 +03:00
rc = 1 ;
goto out ;
}
2009-08-14 07:59:50 -04:00
if ( have & DKD_HAVE_UID ) {
2009-08-14 07:59:49 -04:00
rc = setuid ( arg . uid ) ;
2007-12-05 18:23:39 +03:00
if ( rc = = - 1 ) {
2009-08-14 07:59:49 -04:00
syslog ( LOG_ERR , " setuid: %s " , strerror ( errno ) ) ;
2007-12-05 18:23:39 +03:00
goto out ;
}
2009-09-04 06:29:44 -04:00
ccname = find_krb5_cc ( CIFS_DEFAULT_KRB5_DIR , arg . uid ) ;
}
2009-08-14 07:59:50 -04:00
2009-08-26 06:26:02 -04:00
host = arg . hostname ;
2009-08-14 07:59:50 -04:00
2007-12-05 18:23:39 +03:00
// do mech specific authorization
2009-08-14 07:59:49 -04:00
switch ( arg . sec ) {
2008-08-19 21:29:41 -04:00
case MS_KRB5 :
2009-08-14 07:59:49 -04:00
case KRB5 :
2009-08-26 06:26:02 -04:00
retry_new_hostname :
2009-08-14 07:59:49 -04:00
/* for "cifs/" service name + terminating 0 */
2009-08-26 06:26:02 -04:00
datalen = strlen ( host ) + 5 + 1 ;
2009-08-14 07:59:49 -04:00
princ = SMB_XMALLOC_ARRAY ( char , datalen ) ;
if ( ! princ ) {
2009-08-26 06:26:02 -04:00
rc = - ENOMEM ;
2007-12-05 18:23:39 +03:00
break ;
}
2009-08-14 07:59:49 -04:00
2009-08-14 07:59:49 -04:00
if ( arg . sec = = MS_KRB5 )
2009-08-14 07:59:49 -04:00
oid = OID_KERBEROS5_OLD ;
else
oid = OID_KERBEROS5 ;
2009-08-14 07:59:50 -04:00
/*
* try getting a cifs / principal first and then fall back to
* getting a host / principal if that doesn ' t work .
*/
strlcpy ( princ , " cifs/ " , datalen ) ;
2009-08-26 06:26:02 -04:00
strlcpy ( princ + 5 , host , datalen - 5 ) ;
2009-08-14 07:59:49 -04:00
rc = handle_krb5_mech ( oid , princ , & secblob , & sess_key , ccname ) ;
2009-08-26 06:26:02 -04:00
if ( ! rc )
break ;
memcpy ( princ , " host/ " , 5 ) ;
rc = handle_krb5_mech ( oid , princ , & secblob , & sess_key , ccname ) ;
if ( ! rc )
break ;
if ( ! try_dns | | ! ( have & DKD_HAVE_IP ) )
break ;
rc = ip_to_fqdn ( arg . ip , hostbuf , sizeof ( hostbuf ) ) ;
if ( rc )
break ;
2009-08-14 07:59:49 -04:00
SAFE_FREE ( princ ) ;
2009-08-26 06:26:02 -04:00
try_dns = 0 ;
host = hostbuf ;
goto retry_new_hostname ;
2009-08-14 07:59:49 -04:00
default :
2009-08-14 07:59:49 -04:00
syslog ( LOG_ERR , " sectype: %d is not implemented " , arg . sec ) ;
2009-08-14 07:59:49 -04:00
rc = 1 ;
break ;
2007-12-05 18:23:39 +03:00
}
2009-08-26 06:26:02 -04:00
SAFE_FREE ( princ ) ;
2009-08-14 07:59:49 -04:00
if ( rc )
2007-12-05 18:23:39 +03:00
goto out ;
/* pack SecurityBLob and SessionKey into downcall packet */
datalen =
sizeof ( struct cifs_spnego_msg ) + secblob . length + sess_key . length ;
keydata = ( struct cifs_spnego_msg * ) SMB_XMALLOC_ARRAY ( char , datalen ) ;
if ( ! keydata ) {
rc = 1 ;
goto out ;
}
2009-08-14 07:59:49 -04:00
keydata - > version = arg . ver ;
2007-12-05 18:23:39 +03:00
keydata - > flags = 0 ;
keydata - > sesskey_len = sess_key . length ;
keydata - > secblob_len = secblob . length ;
memcpy ( & ( keydata - > data ) , sess_key . data , sess_key . length ) ;
memcpy ( & ( keydata - > data ) + keydata - > sesskey_len ,
secblob . data , secblob . length ) ;
/* setup key */
rc = keyctl_instantiate ( key , keydata , datalen , 0 ) ;
if ( rc = = - 1 ) {
2009-08-14 07:59:49 -04:00
syslog ( LOG_ERR , " keyctl_instantiate: %s " , strerror ( errno ) ) ;
2007-12-05 18:23:39 +03:00
goto out ;
}
/* BB: maybe we need use timeout for key: for example no more then
* ticket lifietime ? */
/* keyctl_set_timeout( key, 60); */
2008-08-12 14:32:54 -04:00
out :
/*
* on error , negatively instantiate the key ourselves so that we can
* make sure the kernel doesn ' t hang it off of a searchable keyring
* and interfere with the next attempt to instantiate the key .
*/
if ( rc ! = 0 & & key = = 0 )
keyctl_negate ( key , 1 , KEY_REQKEY_DEFL_DEFAULT ) ;
2007-12-05 18:23:39 +03:00
data_blob_free ( & secblob ) ;
data_blob_free ( & sess_key ) ;
2009-07-09 21:04:08 -04:00
SAFE_FREE ( ccname ) ;
2009-08-14 07:59:49 -04:00
SAFE_FREE ( arg . hostname ) ;
2009-08-26 06:26:02 -04:00
SAFE_FREE ( arg . ip ) ;
2007-12-05 18:23:39 +03:00
SAFE_FREE ( keydata ) ;
return rc ;
}