2004-06-20 04:58:09 +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
2007-07-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2004-06-20 04:58:09 +04:00
( 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
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2004-06-20 04:58:09 +04:00
*/
# include "includes.h"
2006-11-07 03:48:36 +03:00
# include "auth/gensec/spnego.h"
# include "auth/gensec/gensec.h"
2008-10-11 23:31:42 +04:00
# include "../lib/util/asn1.h"
2004-06-20 04:58:09 +04:00
2007-10-07 02:16:19 +04:00
static bool read_negTokenInit ( struct asn1_data * asn1 , TALLOC_CTX * mem_ctx ,
2007-05-21 10:12:06 +04:00
struct spnego_negTokenInit * token )
2004-06-20 04:58:09 +04:00
{
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 ;
2004-07-06 05:28:12 +04:00
uint8_t context ;
if ( ! asn1_peek_uint8 ( asn1 , & context ) ) {
2007-10-07 02:16:19 +04:00
asn1 - > has_error = true ;
2004-07-06 05:28:12 +04:00
break ;
}
2004-06-20 04:58:09 +04:00
2004-07-06 05:28:12 +04:00
switch ( context ) {
2004-06-20 04:58:09 +04:00
/* Read mechTypes */
case ASN1_CONTEXT ( 0 ) :
asn1_start_tag ( asn1 , ASN1_CONTEXT ( 0 ) ) ;
asn1_start_tag ( asn1 , ASN1_SEQUENCE ( 0 ) ) ;
2005-01-27 10:08:20 +03:00
token - > mechTypes = talloc ( NULL , const char * ) ;
2004-06-20 04:58:09 +04:00
for ( i = 0 ; ! asn1 - > has_error & &
0 < asn1_tag_remaining ( asn1 ) ; i + + ) {
2005-01-07 07:39:16 +03:00
token - > mechTypes = talloc_realloc ( NULL ,
token - > mechTypes ,
const char * , i + 2 ) ;
2007-05-21 10:12:06 +04:00
asn1_read_OID ( asn1 , token - > mechTypes , token - > mechTypes + i ) ;
2004-06-20 04:58:09 +04:00
}
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 ) ) ;
2007-05-21 10:12:06 +04:00
asn1_read_OctetString ( asn1 , mem_ctx , & token - > mechToken ) ;
2004-06-20 04:58:09 +04:00
asn1_end_tag ( asn1 ) ;
break ;
/* Read mecListMIC */
case ASN1_CONTEXT ( 3 ) :
2004-07-06 05:28:12 +04:00
{
uint8_t type_peek ;
2004-06-20 04:58:09 +04:00
asn1_start_tag ( asn1 , ASN1_CONTEXT ( 3 ) ) ;
2004-07-06 05:28:12 +04:00
if ( ! asn1_peek_uint8 ( asn1 , & type_peek ) ) {
2007-10-07 02:16:19 +04:00
asn1 - > has_error = true ;
2004-07-06 05:28:12 +04:00
break ;
}
if ( type_peek = = ASN1_OCTET_STRING ) {
2007-05-21 10:12:06 +04:00
asn1_read_OctetString ( asn1 , mem_ctx ,
2004-06-20 04:58:09 +04:00
& 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 ) ) ;
2007-05-21 10:12:06 +04:00
asn1_read_GeneralString ( asn1 , mem_ctx , & mechListMIC ) ;
2004-06-20 04:58:09 +04:00
asn1_pop_tag ( asn1 ) ;
asn1_pop_tag ( asn1 ) ;
2004-07-11 14:20:42 +04:00
token - > targetPrincipal = mechListMIC ;
2004-06-20 04:58:09 +04:00
}
asn1_end_tag ( asn1 ) ;
break ;
2004-07-06 05:28:12 +04:00
}
2004-06-20 04:58:09 +04:00
default :
2007-10-07 02:16:19 +04:00
asn1 - > has_error = true ;
2004-06-20 04:58:09 +04:00
break ;
}
}
asn1_end_tag ( asn1 ) ;
asn1_end_tag ( asn1 ) ;
return ! asn1 - > has_error ;
}
2007-10-07 02:16:19 +04:00
static bool write_negTokenInit ( struct asn1_data * asn1 , struct spnego_negTokenInit * token )
2004-06-20 04:58:09 +04:00
{
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 ) ) ;
#if 0
/* This is what RFC 2478 says ... */
asn1_write_OctetString ( asn1 , token - > mechListMIC . data ,
token - > mechListMIC . length ) ;
# 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
asn1_pop_tag ( asn1 ) ;
}
asn1_pop_tag ( asn1 ) ;
asn1_pop_tag ( asn1 ) ;
return ! asn1 - > has_error ;
}
2007-10-07 02:16:19 +04:00
static bool read_negTokenTarg ( struct asn1_data * asn1 , TALLOC_CTX * mem_ctx ,
2007-05-21 10:12:06 +04:00
struct spnego_negTokenTarg * token )
2004-06-20 04:58:09 +04:00
{
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 ) ) {
2004-07-06 05:28:12 +04:00
uint8_t context ;
if ( ! asn1_peek_uint8 ( asn1 , & context ) ) {
2007-10-07 02:16:19 +04:00
asn1 - > has_error = true ;
2004-07-06 05:28:12 +04:00
break ;
}
switch ( context ) {
2004-06-20 04:58:09 +04:00
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 ) ) ;
2007-05-21 10:12:06 +04:00
asn1_read_OID ( asn1 , mem_ctx , & token - > supportedMech ) ;
2004-06-20 04:58:09 +04:00
asn1_end_tag ( asn1 ) ;
break ;
case ASN1_CONTEXT ( 2 ) :
asn1_start_tag ( asn1 , ASN1_CONTEXT ( 2 ) ) ;
2007-05-21 10:12:06 +04:00
asn1_read_OctetString ( asn1 , mem_ctx , & token - > responseToken ) ;
2004-06-20 04:58:09 +04:00
asn1_end_tag ( asn1 ) ;
break ;
case ASN1_CONTEXT ( 3 ) :
asn1_start_tag ( asn1 , ASN1_CONTEXT ( 3 ) ) ;
2007-05-21 10:12:06 +04:00
asn1_read_OctetString ( asn1 , mem_ctx , & token - > mechListMIC ) ;
2004-06-20 04:58:09 +04:00
asn1_end_tag ( asn1 ) ;
break ;
default :
2007-10-07 02:16:19 +04:00
asn1 - > has_error = true ;
2004-06-20 04:58:09 +04:00
break ;
}
}
asn1_end_tag ( asn1 ) ;
asn1_end_tag ( asn1 ) ;
return ! asn1 - > has_error ;
}
2007-10-07 02:16:19 +04:00
static bool write_negTokenTarg ( struct asn1_data * asn1 , struct spnego_negTokenTarg * token )
2004-06-20 04:58:09 +04:00
{
asn1_push_tag ( asn1 , ASN1_CONTEXT ( 1 ) ) ;
asn1_push_tag ( asn1 , ASN1_SEQUENCE ( 0 ) ) ;
2004-10-27 16:59:41 +04:00
if ( token - > negResult ! = SPNEGO_NONE_RESULT ) {
asn1_push_tag ( asn1 , ASN1_CONTEXT ( 0 ) ) ;
asn1_write_enumerated ( asn1 , token - > negResult ) ;
asn1_pop_tag ( asn1 ) ;
}
2004-06-20 04:58:09 +04:00
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 ;
}
2007-05-21 10:12:06 +04:00
ssize_t spnego_read_data ( TALLOC_CTX * mem_ctx , DATA_BLOB data , struct spnego_data * token )
2004-06-20 04:58:09 +04:00
{
2007-05-21 16:47:18 +04:00
struct asn1_data * asn1 ;
2004-06-20 04:58:09 +04:00
ssize_t ret = - 1 ;
2004-07-10 03:38:13 +04:00
uint8_t context ;
2004-06-20 04:58:09 +04:00
ZERO_STRUCTP ( token ) ;
2004-07-06 05:01:39 +04:00
if ( data . length = = 0 ) {
return ret ;
}
2007-05-21 16:47:18 +04:00
asn1 = asn1_init ( mem_ctx ) ;
if ( asn1 = = NULL ) {
return - 1 ;
}
2007-05-21 10:12:06 +04:00
asn1_load ( asn1 , data ) ;
2004-06-20 04:58:09 +04:00
2007-05-21 10:12:06 +04:00
if ( ! asn1_peek_uint8 ( asn1 , & context ) ) {
2007-10-07 02:16:19 +04:00
asn1 - > has_error = true ;
2004-07-06 05:28:12 +04:00
} else {
switch ( context ) {
case ASN1_APPLICATION ( 0 ) :
2007-05-21 10:12:06 +04:00
asn1_start_tag ( asn1 , ASN1_APPLICATION ( 0 ) ) ;
asn1_check_OID ( asn1 , GENSEC_OID_SPNEGO ) ;
if ( read_negTokenInit ( asn1 , mem_ctx , & token - > negTokenInit ) ) {
2004-07-06 05:28:12 +04:00
token - > type = SPNEGO_NEG_TOKEN_INIT ;
}
2007-05-21 10:12:06 +04:00
asn1_end_tag ( asn1 ) ;
2004-07-06 05:28:12 +04:00
break ;
case ASN1_CONTEXT ( 1 ) :
2007-05-21 10:12:06 +04:00
if ( read_negTokenTarg ( asn1 , mem_ctx , & token - > negTokenTarg ) ) {
2004-07-06 05:28:12 +04:00
token - > type = SPNEGO_NEG_TOKEN_TARG ;
}
break ;
default :
2007-10-07 02:16:19 +04:00
asn1 - > has_error = true ;
2004-07-06 05:28:12 +04:00
break ;
2004-06-20 04:58:09 +04:00
}
}
2007-05-21 10:12:06 +04:00
if ( ! asn1 - > has_error ) ret = asn1 - > ofs ;
asn1_free ( asn1 ) ;
2004-06-20 04:58:09 +04:00
return ret ;
}
2004-06-23 19:32:44 +04:00
ssize_t spnego_write_data ( TALLOC_CTX * mem_ctx , DATA_BLOB * blob , struct spnego_data * spnego )
2004-06-20 04:58:09 +04:00
{
2007-05-21 10:12:06 +04:00
struct asn1_data * asn1 = asn1_init ( mem_ctx ) ;
2004-06-20 04:58:09 +04:00
ssize_t ret = - 1 ;
2007-05-21 16:47:18 +04:00
if ( asn1 = = NULL ) {
return - 1 ;
}
2004-06-20 04:58:09 +04:00
switch ( spnego - > type ) {
case SPNEGO_NEG_TOKEN_INIT :
2007-05-21 10:12:06 +04:00
asn1_push_tag ( asn1 , ASN1_APPLICATION ( 0 ) ) ;
asn1_write_OID ( asn1 , GENSEC_OID_SPNEGO ) ;
write_negTokenInit ( asn1 , & spnego - > negTokenInit ) ;
asn1_pop_tag ( asn1 ) ;
2004-06-20 04:58:09 +04:00
break ;
case SPNEGO_NEG_TOKEN_TARG :
2007-05-21 10:12:06 +04:00
write_negTokenTarg ( asn1 , & spnego - > negTokenTarg ) ;
2004-06-20 04:58:09 +04:00
break ;
default :
2007-10-07 02:16:19 +04:00
asn1 - > has_error = true ;
2004-06-20 04:58:09 +04:00
break ;
}
2007-05-21 10:12:06 +04:00
if ( ! asn1 - > has_error ) {
* blob = data_blob_talloc ( mem_ctx , asn1 - > data , asn1 - > length ) ;
ret = asn1 - > ofs ;
2004-06-20 04:58:09 +04:00
}
2007-05-21 10:12:06 +04:00
asn1_free ( asn1 ) ;
2004-06-20 04:58:09 +04:00
return ret ;
}
2007-10-07 02:16:19 +04:00
bool spnego_free_data ( struct spnego_data * spnego )
2004-06-20 04:58:09 +04:00
{
2007-10-07 02:16:19 +04:00
bool ret = true ;
2004-06-20 04:58:09 +04:00
if ( ! spnego ) goto out ;
switch ( spnego - > type ) {
case SPNEGO_NEG_TOKEN_INIT :
if ( spnego - > negTokenInit . mechTypes ) {
2004-08-25 06:04:35 +04:00
talloc_free ( spnego - > negTokenInit . mechTypes ) ;
2004-06-20 04:58:09 +04:00
}
data_blob_free ( & spnego - > negTokenInit . mechToken ) ;
data_blob_free ( & spnego - > negTokenInit . mechListMIC ) ;
2004-08-25 06:25:48 +04:00
talloc_free ( spnego - > negTokenInit . targetPrincipal ) ;
2004-06-20 04:58:09 +04:00
break ;
case SPNEGO_NEG_TOKEN_TARG :
if ( spnego - > negTokenTarg . supportedMech ) {
2004-11-02 14:17:06 +03:00
talloc_free ( discard_const ( spnego - > negTokenTarg . supportedMech ) ) ;
2004-06-20 04:58:09 +04:00
}
data_blob_free ( & spnego - > negTokenTarg . responseToken ) ;
data_blob_free ( & spnego - > negTokenTarg . mechListMIC ) ;
break ;
default :
2007-10-07 02:16:19 +04:00
ret = false ;
2004-06-20 04:58:09 +04:00
break ;
}
ZERO_STRUCTP ( spnego ) ;
out :
return ret ;
}
2008-08-12 16:26:21 +04:00
bool spnego_write_mech_types ( TALLOC_CTX * mem_ctx ,
const char * * mech_types ,
DATA_BLOB * blob )
{
struct asn1_data * asn1 = asn1_init ( mem_ctx ) ;
/* Write mechTypes */
if ( mech_types & & * mech_types ) {
int i ;
asn1_push_tag ( asn1 , ASN1_SEQUENCE ( 0 ) ) ;
for ( i = 0 ; mech_types [ i ] ; i + + ) {
asn1_write_OID ( asn1 , mech_types [ i ] ) ;
}
asn1_pop_tag ( asn1 ) ;
}
if ( asn1 - > has_error ) {
asn1_free ( asn1 ) ;
return false ;
}
* blob = data_blob_talloc ( mem_ctx , asn1 - > data , asn1 - > length ) ;
if ( blob - > length ! = asn1 - > length ) {
asn1_free ( asn1 ) ;
return false ;
}
asn1_free ( asn1 ) ;
return true ;
}