2003-07-29 19:00:38 +04:00
/*
Unix SMB / CIFS implementation .
RFC2478 Compliant SPNEGO implementation
Copyright ( C ) Jim McDonough < jmcd @ us . ibm . com > 2003
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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_AUTH
static BOOL read_negTokenInit ( ASN1_DATA * asn1 , negTokenInit_t * token )
{
ZERO_STRUCTP ( token ) ;
asn1_start_tag ( asn1 , ASN1_CONTEXT ( 0 ) ) ;
asn1_start_tag ( asn1 , ASN1_SEQUENCE ( 0 ) ) ;
while ( ! asn1 - > has_error & & 0 < asn1_tag_remaining ( asn1 ) ) {
int i ;
switch ( asn1 - > data [ asn1 - > ofs ] ) {
/* Read mechTypes */
case ASN1_CONTEXT ( 0 ) :
asn1_start_tag ( asn1 , ASN1_CONTEXT ( 0 ) ) ;
asn1_start_tag ( asn1 , ASN1_SEQUENCE ( 0 ) ) ;
token - > mechTypes = malloc ( sizeof ( * token - > mechTypes ) ) ;
for ( i = 0 ; ! asn1 - > has_error & &
0 < asn1_tag_remaining ( asn1 ) ; i + + ) {
token - > mechTypes =
2003-07-31 19:53:59 +04:00
realloc ( token - > mechTypes , ( i + 2 ) *
2003-07-29 19:00:38 +04:00
sizeof ( * token - > mechTypes ) ) ;
asn1_read_OID ( asn1 , token - > mechTypes + i ) ;
}
token - > mechTypes [ i ] = NULL ;
asn1_end_tag ( asn1 ) ;
asn1_end_tag ( asn1 ) ;
break ;
/* Read reqFlags */
case ASN1_CONTEXT ( 1 ) :
asn1_start_tag ( asn1 , ASN1_CONTEXT ( 1 ) ) ;
asn1_read_Integer ( asn1 , & token - > reqFlags ) ;
token - > reqFlags | = SPNEGO_REQ_FLAG ;
asn1_end_tag ( asn1 ) ;
break ;
/* Read mechToken */
case ASN1_CONTEXT ( 2 ) :
asn1_start_tag ( asn1 , ASN1_CONTEXT ( 2 ) ) ;
asn1_read_OctetString ( asn1 , & token - > mechToken ) ;
asn1_end_tag ( asn1 ) ;
break ;
/* Read mecListMIC */
case ASN1_CONTEXT ( 3 ) :
asn1_start_tag ( asn1 , ASN1_CONTEXT ( 3 ) ) ;
2003-08-04 17:10:43 +04:00
if ( asn1 - > data [ asn1 - > ofs ] = = ASN1_OCTET_STRING ) {
asn1_read_OctetString ( asn1 ,
& token - > mechListMIC ) ;
} else {
/* RFC 2478 says we have an Octet String here,
but W2k sends something different . . . */
char * mechListMIC ;
asn1_push_tag ( asn1 , ASN1_SEQUENCE ( 0 ) ) ;
asn1_push_tag ( asn1 , ASN1_CONTEXT ( 0 ) ) ;
asn1_read_GeneralString ( asn1 , & mechListMIC ) ;
asn1_pop_tag ( asn1 ) ;
asn1_pop_tag ( asn1 ) ;
token - > mechListMIC =
data_blob ( mechListMIC , strlen ( mechListMIC ) ) ;
SAFE_FREE ( mechListMIC ) ;
}
2003-07-29 19:00:38 +04:00
asn1_end_tag ( asn1 ) ;
break ;
default :
asn1 - > has_error = True ;
break ;
}
}
asn1_end_tag ( asn1 ) ;
asn1_end_tag ( asn1 ) ;
return ! asn1 - > has_error ;
}
static BOOL write_negTokenInit ( ASN1_DATA * asn1 , negTokenInit_t * token )
{
asn1_push_tag ( asn1 , ASN1_CONTEXT ( 0 ) ) ;
asn1_push_tag ( asn1 , ASN1_SEQUENCE ( 0 ) ) ;
/* Write mechTypes */
if ( token - > mechTypes & & * token - > mechTypes ) {
int i ;
asn1_push_tag ( asn1 , ASN1_CONTEXT ( 0 ) ) ;
asn1_push_tag ( asn1 , ASN1_SEQUENCE ( 0 ) ) ;
for ( i = 0 ; token - > mechTypes [ i ] ; i + + ) {
asn1_write_OID ( asn1 , token - > mechTypes [ i ] ) ;
}
asn1_pop_tag ( asn1 ) ;
asn1_pop_tag ( asn1 ) ;
}
/* write reqFlags */
if ( token - > reqFlags & SPNEGO_REQ_FLAG ) {
int flags = token - > reqFlags & ~ SPNEGO_REQ_FLAG ;
asn1_push_tag ( asn1 , ASN1_CONTEXT ( 1 ) ) ;
asn1_write_Integer ( asn1 , flags ) ;
asn1_pop_tag ( asn1 ) ;
}
/* write mechToken */
if ( token - > mechToken . data ) {
asn1_push_tag ( asn1 , ASN1_CONTEXT ( 2 ) ) ;
asn1_write_OctetString ( asn1 , token - > mechToken . data ,
token - > mechToken . length ) ;
asn1_pop_tag ( asn1 ) ;
}
/* write mechListMIC */
if ( token - > mechListMIC . data ) {
asn1_push_tag ( asn1 , ASN1_CONTEXT ( 3 ) ) ;
2003-08-15 06:57:59 +04:00
#if 0
/* This is what RFC 2478 says ... */
2003-07-29 19:00:38 +04:00
asn1_write_OctetString ( asn1 , token - > mechListMIC . data ,
token - > mechListMIC . length ) ;
2003-08-15 06:57:59 +04:00
# else
/* ... but unfortunately this is what Windows
sends / expects */
asn1_push_tag ( asn1 , ASN1_SEQUENCE ( 0 ) ) ;
asn1_push_tag ( asn1 , ASN1_CONTEXT ( 0 ) ) ;
asn1_push_tag ( asn1 , ASN1_GENERAL_STRING ) ;
asn1_write ( asn1 , token - > mechListMIC . data ,
token - > mechListMIC . length ) ;
asn1_pop_tag ( asn1 ) ;
asn1_pop_tag ( asn1 ) ;
asn1_pop_tag ( asn1 ) ;
# endif
2003-07-29 19:00:38 +04:00
asn1_pop_tag ( asn1 ) ;
}
asn1_pop_tag ( asn1 ) ;
asn1_pop_tag ( asn1 ) ;
return ! asn1 - > has_error ;
}
static BOOL read_negTokenTarg ( ASN1_DATA * asn1 , negTokenTarg_t * token )
{
ZERO_STRUCTP ( token ) ;
asn1_start_tag ( asn1 , ASN1_CONTEXT ( 1 ) ) ;
asn1_start_tag ( asn1 , ASN1_SEQUENCE ( 0 ) ) ;
while ( ! asn1 - > has_error & & 0 < asn1_tag_remaining ( asn1 ) ) {
switch ( asn1 - > data [ asn1 - > ofs ] ) {
case ASN1_CONTEXT ( 0 ) :
asn1_start_tag ( asn1 , ASN1_CONTEXT ( 0 ) ) ;
asn1_start_tag ( asn1 , ASN1_ENUMERATED ) ;
asn1_read_uint8 ( asn1 , & token - > negResult ) ;
asn1_end_tag ( asn1 ) ;
asn1_end_tag ( asn1 ) ;
break ;
case ASN1_CONTEXT ( 1 ) :
asn1_start_tag ( asn1 , ASN1_CONTEXT ( 1 ) ) ;
asn1_read_OID ( asn1 , & token - > supportedMech ) ;
asn1_end_tag ( asn1 ) ;
break ;
case ASN1_CONTEXT ( 2 ) :
asn1_start_tag ( asn1 , ASN1_CONTEXT ( 2 ) ) ;
asn1_read_OctetString ( asn1 , & token - > responseToken ) ;
asn1_end_tag ( asn1 ) ;
break ;
case ASN1_CONTEXT ( 3 ) :
asn1_start_tag ( asn1 , ASN1_CONTEXT ( 3 ) ) ;
asn1_read_OctetString ( asn1 , & token - > mechListMIC ) ;
asn1_end_tag ( asn1 ) ;
break ;
default :
asn1 - > has_error = True ;
break ;
}
}
asn1_end_tag ( asn1 ) ;
asn1_end_tag ( asn1 ) ;
return ! asn1 - > has_error ;
}
static BOOL write_negTokenTarg ( ASN1_DATA * asn1 , negTokenTarg_t * token )
{
asn1_push_tag ( asn1 , ASN1_CONTEXT ( 1 ) ) ;
asn1_push_tag ( asn1 , ASN1_SEQUENCE ( 0 ) ) ;
asn1_push_tag ( asn1 , ASN1_CONTEXT ( 0 ) ) ;
asn1_write_enumerated ( asn1 , token - > negResult ) ;
asn1_pop_tag ( asn1 ) ;
if ( token - > supportedMech ) {
asn1_push_tag ( asn1 , ASN1_CONTEXT ( 1 ) ) ;
asn1_write_OID ( asn1 , token - > supportedMech ) ;
asn1_pop_tag ( asn1 ) ;
}
if ( token - > responseToken . data ) {
asn1_push_tag ( asn1 , ASN1_CONTEXT ( 2 ) ) ;
asn1_write_OctetString ( asn1 , token - > responseToken . data ,
token - > responseToken . length ) ;
asn1_pop_tag ( asn1 ) ;
}
if ( token - > mechListMIC . data ) {
asn1_push_tag ( asn1 , ASN1_CONTEXT ( 3 ) ) ;
asn1_write_OctetString ( asn1 , token - > mechListMIC . data ,
token - > mechListMIC . length ) ;
asn1_pop_tag ( asn1 ) ;
}
asn1_pop_tag ( asn1 ) ;
asn1_pop_tag ( asn1 ) ;
return ! asn1 - > has_error ;
}
ssize_t read_spnego_data ( DATA_BLOB data , SPNEGO_DATA * token )
{
ASN1_DATA asn1 ;
ssize_t ret = - 1 ;
ZERO_STRUCTP ( token ) ;
ZERO_STRUCT ( asn1 ) ;
asn1_load ( & asn1 , data ) ;
switch ( asn1 . data [ asn1 . ofs ] ) {
case ASN1_APPLICATION ( 0 ) :
asn1_start_tag ( & asn1 , ASN1_APPLICATION ( 0 ) ) ;
asn1_check_OID ( & asn1 , OID_SPNEGO ) ;
if ( read_negTokenInit ( & asn1 , & token - > negTokenInit ) ) {
token - > type = SPNEGO_NEG_TOKEN_INIT ;
}
asn1_end_tag ( & asn1 ) ;
break ;
case ASN1_CONTEXT ( 1 ) :
if ( read_negTokenTarg ( & asn1 , & token - > negTokenTarg ) ) {
token - > type = SPNEGO_NEG_TOKEN_TARG ;
}
break ;
default :
break ;
}
if ( ! asn1 . has_error ) ret = asn1 . ofs ;
asn1_free ( & asn1 ) ;
return ret ;
}
ssize_t write_spnego_data ( DATA_BLOB * blob , SPNEGO_DATA * spnego )
{
ASN1_DATA asn1 ;
ssize_t ret = - 1 ;
ZERO_STRUCT ( asn1 ) ;
switch ( spnego - > type ) {
case SPNEGO_NEG_TOKEN_INIT :
asn1_push_tag ( & asn1 , ASN1_APPLICATION ( 0 ) ) ;
asn1_write_OID ( & asn1 , OID_SPNEGO ) ;
write_negTokenInit ( & asn1 , & spnego - > negTokenInit ) ;
asn1_pop_tag ( & asn1 ) ;
break ;
case SPNEGO_NEG_TOKEN_TARG :
write_negTokenTarg ( & asn1 , & spnego - > negTokenTarg ) ;
break ;
default :
asn1 . has_error = True ;
break ;
}
if ( ! asn1 . has_error ) {
* blob = data_blob ( asn1 . data , asn1 . length ) ;
ret = asn1 . ofs ;
}
asn1_free ( & asn1 ) ;
return ret ;
}
2003-07-31 14:23:13 +04:00
BOOL free_spnego_data ( SPNEGO_DATA * spnego )
{
BOOL ret = True ;
if ( ! spnego ) goto out ;
switch ( spnego - > type ) {
case SPNEGO_NEG_TOKEN_INIT :
if ( spnego - > negTokenInit . mechTypes ) {
int i ;
for ( i = 0 ; spnego - > negTokenInit . mechTypes [ i ] ; i + + ) {
free ( spnego - > negTokenInit . mechTypes [ i ] ) ;
}
free ( spnego - > negTokenInit . mechTypes ) ;
}
data_blob_free ( & spnego - > negTokenInit . mechToken ) ;
data_blob_free ( & spnego - > negTokenInit . mechListMIC ) ;
break ;
case SPNEGO_NEG_TOKEN_TARG :
if ( spnego - > negTokenTarg . supportedMech ) {
free ( spnego - > negTokenTarg . supportedMech ) ;
}
data_blob_free ( & spnego - > negTokenTarg . responseToken ) ;
data_blob_free ( & spnego - > negTokenTarg . mechListMIC ) ;
break ;
default :
ret = False ;
break ;
}
ZERO_STRUCTP ( spnego ) ;
out :
return ret ;
}