2021-03-16 10:49:09 +09:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* The ASB .1 / BER parsing code is derived from ip_nat_snmp_basic . c which was in
* turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich
*
* Copyright ( c ) 2000 RP Internet ( www . rpi . net . au ) .
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/slab.h>
2021-04-19 17:26:15 +09:00
# include <linux/oid_registry.h>
2021-03-16 10:49:09 +09:00
# include "glob.h"
# include "asn1.h"
# include "connection.h"
# include "auth.h"
2021-06-09 10:06:57 +09:00
# include "ksmbd_spnego_negtokeninit.asn1.h"
# include "ksmbd_spnego_negtokentarg.asn1.h"
2021-03-16 10:49:09 +09:00
# define SPNEGO_OID_LEN 7
# define NTLMSSP_OID_LEN 10
# define KRB5_OID_LEN 7
# define KRB5U2U_OID_LEN 8
# define MSKRB5_OID_LEN 7
static unsigned long SPNEGO_OID [ 7 ] = { 1 , 3 , 6 , 1 , 5 , 5 , 2 } ;
static unsigned long NTLMSSP_OID [ 10 ] = { 1 , 3 , 6 , 1 , 4 , 1 , 311 , 2 , 2 , 10 } ;
static unsigned long KRB5_OID [ 7 ] = { 1 , 2 , 840 , 113554 , 1 , 2 , 2 } ;
static unsigned long KRB5U2U_OID [ 8 ] = { 1 , 2 , 840 , 113554 , 1 , 2 , 2 , 3 } ;
static unsigned long MSKRB5_OID [ 7 ] = { 1 , 2 , 840 , 48018 , 1 , 2 , 2 } ;
static char NTLMSSP_OID_STR [ NTLMSSP_OID_LEN ] = { 0x2b , 0x06 , 0x01 , 0x04 , 0x01 ,
0x82 , 0x37 , 0x02 , 0x02 , 0x0a } ;
2021-04-19 17:26:15 +09:00
static bool
asn1_subid_decode ( const unsigned char * * begin , const unsigned char * end ,
2021-05-26 17:57:12 +09:00
unsigned long * subid )
2021-03-16 10:49:09 +09:00
{
2021-04-19 17:26:15 +09:00
const unsigned char * ptr = * begin ;
2021-03-16 10:49:09 +09:00
unsigned char ch ;
* subid = 0 ;
do {
2021-04-19 17:26:15 +09:00
if ( ptr > = end )
return false ;
2021-03-16 10:49:09 +09:00
2021-04-19 17:26:15 +09:00
ch = * ptr + + ;
2021-03-16 10:49:09 +09:00
* subid < < = 7 ;
* subid | = ch & 0x7F ;
} while ( ( ch & 0x80 ) = = 0x80 ) ;
2021-04-19 17:26:15 +09:00
* begin = ptr ;
return true ;
2021-03-16 10:49:09 +09:00
}
2021-04-19 17:26:15 +09:00
static bool asn1_oid_decode ( const unsigned char * value , size_t vlen ,
2021-05-26 17:57:12 +09:00
unsigned long * * oid , size_t * oidlen )
2021-03-16 10:49:09 +09:00
{
2021-04-19 17:26:15 +09:00
const unsigned char * iptr = value , * end = value + vlen ;
2021-03-16 10:49:09 +09:00
unsigned long * optr ;
2021-04-19 17:26:15 +09:00
unsigned long subid ;
2021-03-16 10:49:09 +09:00
2021-04-19 17:26:15 +09:00
vlen + = 1 ;
2021-05-26 16:42:12 +09:00
if ( vlen < 2 | | vlen > UINT_MAX / sizeof ( unsigned long ) )
2021-06-18 09:54:53 +09:00
goto fail_nullify ;
2021-03-16 10:49:09 +09:00
2021-04-19 17:26:15 +09:00
* oid = kmalloc ( vlen * sizeof ( unsigned long ) , GFP_KERNEL ) ;
2021-03-16 10:49:09 +09:00
if ( ! * oid )
2021-04-19 17:26:15 +09:00
return false ;
2021-03-16 10:49:09 +09:00
optr = * oid ;
2021-05-26 15:22:37 +09:00
if ( ! asn1_subid_decode ( & iptr , end , & subid ) )
goto fail ;
2021-03-16 10:49:09 +09:00
if ( subid < 40 ) {
optr [ 0 ] = 0 ;
optr [ 1 ] = subid ;
} else if ( subid < 80 ) {
optr [ 0 ] = 1 ;
optr [ 1 ] = subid - 40 ;
} else {
optr [ 0 ] = 2 ;
optr [ 1 ] = subid - 80 ;
}
2021-04-19 17:26:15 +09:00
* oidlen = 2 ;
2021-03-16 10:49:09 +09:00
optr + = 2 ;
2021-04-19 17:26:15 +09:00
while ( iptr < end ) {
2021-05-26 15:22:37 +09:00
if ( + + ( * oidlen ) > vlen )
goto fail ;
2021-03-16 10:49:09 +09:00
2021-05-26 15:22:37 +09:00
if ( ! asn1_subid_decode ( & iptr , end , optr + + ) )
goto fail ;
2021-03-16 10:49:09 +09:00
}
2021-04-19 17:26:15 +09:00
return true ;
2021-05-26 15:22:37 +09:00
fail :
kfree ( * oid ) ;
2021-06-18 09:54:53 +09:00
fail_nullify :
2021-05-26 15:22:37 +09:00
* oid = NULL ;
return false ;
2021-03-16 10:49:09 +09:00
}
2021-05-26 17:57:12 +09:00
static bool oid_eq ( unsigned long * oid1 , unsigned int oid1len ,
unsigned long * oid2 , unsigned int oid2len )
2021-03-16 10:49:09 +09:00
{
if ( oid1len ! = oid2len )
2021-04-19 17:26:15 +09:00
return false ;
2021-03-16 10:49:09 +09:00
2021-05-26 15:23:55 +09:00
return memcmp ( oid1 , oid2 , oid1len ) = = 0 ;
2021-03-16 10:49:09 +09:00
}
int
ksmbd_decode_negTokenInit ( unsigned char * security_blob , int length ,
2021-05-26 17:57:12 +09:00
struct ksmbd_conn * conn )
2021-03-16 10:49:09 +09:00
{
2021-06-09 10:06:57 +09:00
return asn1_ber_decoder ( & ksmbd_spnego_negtokeninit_decoder , conn ,
2021-04-19 17:26:15 +09:00
security_blob , length ) ;
2021-03-16 10:49:09 +09:00
}
int
ksmbd_decode_negTokenTarg ( unsigned char * security_blob , int length ,
2021-05-26 17:57:12 +09:00
struct ksmbd_conn * conn )
2021-03-16 10:49:09 +09:00
{
2021-06-09 10:06:57 +09:00
return asn1_ber_decoder ( & ksmbd_spnego_negtokentarg_decoder , conn ,
2021-04-19 17:26:15 +09:00
security_blob , length ) ;
2021-03-16 10:49:09 +09:00
}
static int compute_asn_hdr_len_bytes ( int len )
{
if ( len > 0xFFFFFF )
return 4 ;
else if ( len > 0xFFFF )
return 3 ;
else if ( len > 0xFF )
return 2 ;
else if ( len > 0x7F )
return 1 ;
else
return 0 ;
}
2021-05-26 17:57:12 +09:00
static void encode_asn_tag ( char * buf , unsigned int * ofs , char tag , char seq ,
2021-03-16 10:49:09 +09:00
int length )
{
int i ;
int index = * ofs ;
char hdr_len = compute_asn_hdr_len_bytes ( length ) ;
int len = length + 2 + hdr_len ;
/* insert tag */
buf [ index + + ] = tag ;
2021-05-26 16:40:39 +09:00
if ( ! hdr_len ) {
2021-03-16 10:49:09 +09:00
buf [ index + + ] = len ;
2021-05-26 16:40:39 +09:00
} else {
2021-03-16 10:49:09 +09:00
buf [ index + + ] = 0x80 | hdr_len ;
for ( i = hdr_len - 1 ; i > = 0 ; i - - )
buf [ index + + ] = ( len > > ( i * 8 ) ) & 0xFF ;
}
/* insert seq */
len = len - ( index - * ofs ) ;
buf [ index + + ] = seq ;
2021-05-26 16:40:39 +09:00
if ( ! hdr_len ) {
2021-03-16 10:49:09 +09:00
buf [ index + + ] = len ;
2021-05-26 16:40:39 +09:00
} else {
2021-03-16 10:49:09 +09:00
buf [ index + + ] = 0x80 | hdr_len ;
for ( i = hdr_len - 1 ; i > = 0 ; i - - )
buf [ index + + ] = ( len > > ( i * 8 ) ) & 0xFF ;
}
* ofs + = ( index - * ofs ) ;
}
int build_spnego_ntlmssp_neg_blob ( unsigned char * * pbuffer , u16 * buflen ,
2021-05-26 17:57:12 +09:00
char * ntlm_blob , int ntlm_blob_len )
2021-03-16 10:49:09 +09:00
{
char * buf ;
unsigned int ofs = 0 ;
int neg_result_len = 4 + compute_asn_hdr_len_bytes ( 1 ) * 2 + 1 ;
int oid_len = 4 + compute_asn_hdr_len_bytes ( NTLMSSP_OID_LEN ) * 2 +
NTLMSSP_OID_LEN ;
int ntlmssp_len = 4 + compute_asn_hdr_len_bytes ( ntlm_blob_len ) * 2 +
ntlm_blob_len ;
int total_len = 4 + compute_asn_hdr_len_bytes ( neg_result_len +
oid_len + ntlmssp_len ) * 2 +
neg_result_len + oid_len + ntlmssp_len ;
buf = kmalloc ( total_len , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
/* insert main gss header */
encode_asn_tag ( buf , & ofs , 0xa1 , 0x30 , neg_result_len + oid_len +
ntlmssp_len ) ;
/* insert neg result */
encode_asn_tag ( buf , & ofs , 0xa0 , 0x0a , 1 ) ;
buf [ ofs + + ] = 1 ;
/* insert oid */
encode_asn_tag ( buf , & ofs , 0xa1 , 0x06 , NTLMSSP_OID_LEN ) ;
memcpy ( buf + ofs , NTLMSSP_OID_STR , NTLMSSP_OID_LEN ) ;
ofs + = NTLMSSP_OID_LEN ;
/* insert response token - ntlmssp blob */
encode_asn_tag ( buf , & ofs , 0xa2 , 0x04 , ntlm_blob_len ) ;
memcpy ( buf + ofs , ntlm_blob , ntlm_blob_len ) ;
ofs + = ntlm_blob_len ;
* pbuffer = buf ;
* buflen = total_len ;
return 0 ;
}
int build_spnego_ntlmssp_auth_blob ( unsigned char * * pbuffer , u16 * buflen ,
2021-05-26 17:57:12 +09:00
int neg_result )
2021-03-16 10:49:09 +09:00
{
char * buf ;
unsigned int ofs = 0 ;
int neg_result_len = 4 + compute_asn_hdr_len_bytes ( 1 ) * 2 + 1 ;
int total_len = 4 + compute_asn_hdr_len_bytes ( neg_result_len ) * 2 +
neg_result_len ;
buf = kmalloc ( total_len , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
/* insert main gss header */
encode_asn_tag ( buf , & ofs , 0xa1 , 0x30 , neg_result_len ) ;
/* insert neg result */
encode_asn_tag ( buf , & ofs , 0xa0 , 0x0a , 1 ) ;
if ( neg_result )
buf [ ofs + + ] = 2 ;
else
buf [ ofs + + ] = 0 ;
* pbuffer = buf ;
* buflen = total_len ;
return 0 ;
}
2021-04-19 17:26:15 +09:00
2021-06-09 10:06:57 +09:00
int ksmbd_gssapi_this_mech ( void * context , size_t hdrlen , unsigned char tag ,
const void * value , size_t vlen )
2021-04-19 17:26:15 +09:00
{
unsigned long * oid ;
size_t oidlen ;
int err = 0 ;
if ( ! asn1_oid_decode ( value , vlen , & oid , & oidlen ) ) {
err = - EBADMSG ;
goto out ;
}
if ( ! oid_eq ( oid , oidlen , SPNEGO_OID , SPNEGO_OID_LEN ) )
err = - EBADMSG ;
kfree ( oid ) ;
out :
if ( err ) {
char buf [ 50 ] ;
sprint_oid ( value , vlen , buf , sizeof ( buf ) ) ;
ksmbd_debug ( AUTH , " Unexpected OID: %s \n " , buf ) ;
}
return err ;
}
2021-06-09 10:06:57 +09:00
int ksmbd_neg_token_init_mech_type ( void * context , size_t hdrlen ,
unsigned char tag , const void * value ,
size_t vlen )
2021-04-19 17:26:15 +09:00
{
struct ksmbd_conn * conn = context ;
unsigned long * oid ;
size_t oidlen ;
int mech_type ;
2021-05-26 15:24:39 +09:00
char buf [ 50 ] ;
2021-04-19 17:26:15 +09:00
2021-05-26 15:24:39 +09:00
if ( ! asn1_oid_decode ( value , vlen , & oid , & oidlen ) )
goto fail ;
2021-04-19 17:26:15 +09:00
if ( oid_eq ( oid , oidlen , NTLMSSP_OID , NTLMSSP_OID_LEN ) )
mech_type = KSMBD_AUTH_NTLMSSP ;
else if ( oid_eq ( oid , oidlen , MSKRB5_OID , MSKRB5_OID_LEN ) )
mech_type = KSMBD_AUTH_MSKRB5 ;
else if ( oid_eq ( oid , oidlen , KRB5_OID , KRB5_OID_LEN ) )
mech_type = KSMBD_AUTH_KRB5 ;
else if ( oid_eq ( oid , oidlen , KRB5U2U_OID , KRB5U2U_OID_LEN ) )
mech_type = KSMBD_AUTH_KRB5U2U ;
else
2021-05-26 15:24:39 +09:00
goto fail ;
2021-04-19 17:26:15 +09:00
conn - > auth_mechs | = mech_type ;
if ( conn - > preferred_auth_mech = = 0 )
conn - > preferred_auth_mech = mech_type ;
kfree ( oid ) ;
return 0 ;
2021-05-26 15:24:39 +09:00
fail :
kfree ( oid ) ;
sprint_oid ( value , vlen , buf , sizeof ( buf ) ) ;
ksmbd_debug ( AUTH , " Unexpected OID: %s \n " , buf ) ;
return - EBADMSG ;
2021-04-19 17:26:15 +09:00
}
2021-06-09 10:06:57 +09:00
int ksmbd_neg_token_init_mech_token ( void * context , size_t hdrlen ,
unsigned char tag , const void * value ,
size_t vlen )
2021-04-19 17:26:15 +09:00
{
struct ksmbd_conn * conn = context ;
conn - > mechToken = kmalloc ( vlen + 1 , GFP_KERNEL ) ;
if ( ! conn - > mechToken )
return - ENOMEM ;
memcpy ( conn - > mechToken , value , vlen ) ;
conn - > mechToken [ vlen ] = ' \0 ' ;
return 0 ;
}
2021-06-09 10:06:57 +09:00
int ksmbd_neg_token_targ_resp_token ( void * context , size_t hdrlen ,
unsigned char tag , const void * value ,
size_t vlen )
2021-04-19 17:26:15 +09:00
{
struct ksmbd_conn * conn = context ;
conn - > mechToken = kmalloc ( vlen + 1 , GFP_KERNEL ) ;
if ( ! conn - > mechToken )
return - ENOMEM ;
memcpy ( conn - > mechToken , value , vlen ) ;
conn - > mechToken [ vlen ] = ' \0 ' ;
return 0 ;
}