From 9f7cb41f11c0d2fc09104f6998f75c59bc363b26 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Fri, 12 Oct 2001 04:49:42 +0000 Subject: added NTLMSSP authentication to libsmb. It seems to work well so I have enabled it by default if the server supports it. Let me know if this breaks anything. Choose kerberos with the -k flag to smbclient, otherwise it will use SPNEGO/NTLMSSP/NTLM (This used to be commit 076aa97bee54d182288d9e93ae160ae22a5f7757) --- source3/Makefile.in | 3 +- source3/client/client.c | 5 +- source3/include/asn1.h | 1 + source3/include/client.h | 2 +- source3/libsmb/asn1.c | 27 +++ source3/libsmb/cliconnect.c | 206 +++++++++++++++++------ source3/libsmb/clientgen.c | 5 +- source3/libsmb/clikrb5.c | 221 +------------------------ source3/libsmb/clispnego.c | 395 ++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 590 insertions(+), 275 deletions(-) create mode 100644 source3/libsmb/clispnego.c diff --git a/source3/Makefile.in b/source3/Makefile.in index 30c4cbb44e..b770945d17 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -116,7 +116,8 @@ UBIQX_OBJ = ubiqx/ubi_BinTree.o ubiqx/ubi_Cache.o ubiqx/ubi_SplayTree.o \ PARAM_OBJ = param/loadparm.o param/params.o -LIBSMB_OBJ = libsmb/clientgen.o libsmb/cliconnect.o libsmb/clifile.o libsmb/clikrb5.o libsmb/asn1.o \ +LIBSMB_OBJ = libsmb/clientgen.o libsmb/cliconnect.o libsmb/clifile.o \ + libsmb/clikrb5.o libsmb/clispnego.o libsmb/asn1.o \ libsmb/clirap.o libsmb/clierror.o libsmb/climessage.o \ libsmb/clireadwrite.o libsmb/clilist.o libsmb/cliprint.o \ libsmb/clitrans.o libsmb/clisecdesc.o libsmb/clidgram.o \ diff --git a/source3/client/client.c b/source3/client/client.c index dfcb3d9616..1317346df3 100644 --- a/source3/client/client.c +++ b/source3/client/client.c @@ -2115,10 +2115,7 @@ struct cli_state *do_connect(const char *server, const char *share) } c->protocol = max_protocol; - - if (use_kerberos) { - c->use_spnego = True; - } + c->use_kerberos = use_kerberos; if (!cli_session_request(c, &calling, &called)) { char *p; diff --git a/source3/include/asn1.h b/source3/include/asn1.h index 183a9ced6c..8d9bb158c8 100644 --- a/source3/include/asn1.h +++ b/source3/include/asn1.h @@ -42,5 +42,6 @@ typedef struct { #define ASN1_OCTET_STRING 0x4 #define ASN1_OID 0x6 #define ASN1_BOOLEAN 0x1 +#define ASN1_ENUMERATED 0xa #define ASN1_MAX_OIDS 20 diff --git a/source3/include/client.h b/source3/include/client.h index 24a9d47090..22ea10b9a7 100644 --- a/source3/include/client.h +++ b/source3/include/client.h @@ -128,7 +128,7 @@ struct cli_state { uint16 max_recv_frag; vuser_key key; uint32 ntlmssp_flags; - BOOL use_spnego; /* until we do NTLMSSP we need to make this optional */ + BOOL use_kerberos; BOOL use_oplocks; /* should we use oplocks? */ BOOL use_level_II_oplocks; /* should we use level II oplocks? */ diff --git a/source3/libsmb/asn1.c b/source3/libsmb/asn1.c index 17b1ee1089..6a92a6be00 100644 --- a/source3/libsmb/asn1.c +++ b/source3/libsmb/asn1.c @@ -315,3 +315,30 @@ BOOL asn1_read_GeneralString(ASN1_DATA *data, char **s) asn1_end_tag(data); return !data->has_error; } + +/* read a octet string blob */ +BOOL asn1_read_octet_string(ASN1_DATA *data, DATA_BLOB *blob) +{ + int len; + if (!asn1_start_tag(data, ASN1_OCTET_STRING)) return False; + len = asn1_tag_remaining(data); + blob->data = malloc(len); + if (!blob->data) { + data->has_error = True; + return False; + } + asn1_read(data, blob->data, len); + blob->length = len; + asn1_end_tag(data); + return !data->has_error; +} + +/* check a enumarted value is correct */ +BOOL asn1_check_enumerated(ASN1_DATA *data, int v) +{ + uint8 b; + if (!asn1_start_tag(data, ASN1_ENUMERATED)) return False; + asn1_read_uint8(data, &b); + asn1_end_tag(data); + return !data->has_error && (v == b); +} diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c index 36aedf2d59..11852a09cc 100644 --- a/source3/libsmb/cliconnect.c +++ b/source3/libsmb/cliconnect.c @@ -34,6 +34,7 @@ prots[] = {PROTOCOL_LANMAN1,"LANMAN1.0"}, {PROTOCOL_LANMAN1,"Windows for Workgroups 3.1a"}, {PROTOCOL_LANMAN2,"LM1.2X002"}, + {PROTOCOL_NT1,"Samba"}, {PROTOCOL_NT1,"LANMAN2.1"}, {PROTOCOL_NT1,"NT LM 0.12"}, {-1,NULL} @@ -315,49 +316,17 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, char *user, return True; } -#if HAVE_KRB5 + /**************************************************************************** -do a spnego encrypted session setup +send a extended security session setup blob, returning a reply blob ****************************************************************************/ -static BOOL cli_session_setup_spnego(struct cli_state *cli, char *user, - char *pass, char *workgroup) +static DATA_BLOB cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob) { uint32 capabilities = cli_session_setup_capabilities(cli); char *p; - DATA_BLOB blob2, negTokenTarg; - char *principle; - char *OIDs[ASN1_MAX_OIDS]; - uint8 guid[16]; - int i; - BOOL got_kerberos_mechanism = False; - - /* the server sent us the first part of the SPNEGO exchange in the negprot - reply */ - if (!spnego_parse_negTokenInit(cli->secblob, guid, OIDs, &principle)) { - return False; - } - - /* make sure the server understands kerberos */ - for (i=0;OIDs[i];i++) { - DEBUG(3,("got OID=%s\n", OIDs[i])); - if (strcmp(OIDs[i], "1 2 840 48018 1 2 2") == 0) { - got_kerberos_mechanism = True; - } - free(OIDs[i]); - } - DEBUG(3,("got principle=%s\n", principle)); - - if (!got_kerberos_mechanism) { - DEBUG(1,("Server didn't offer kerberos5 mechanism!?\n")); - return False; - } - - /* generate the encapsulated kerberos5 ticket */ - negTokenTarg = spnego_gen_negTokenTarg(cli, principle); - - free(principle); + DATA_BLOB blob2; - if (!negTokenTarg.data) return False; + blob2 = data_blob(NULL, 0); capabilities |= CAP_EXTENDED_SECURITY; @@ -373,23 +342,24 @@ static BOOL cli_session_setup_spnego(struct cli_state *cli, char *user, SSVAL(cli->outbuf,smb_vwv3,2); SSVAL(cli->outbuf,smb_vwv4,0); SIVAL(cli->outbuf,smb_vwv5,0); - SSVAL(cli->outbuf,smb_vwv7,negTokenTarg.length); + SSVAL(cli->outbuf,smb_vwv7,blob.length); SIVAL(cli->outbuf,smb_vwv10,capabilities); p = smb_buf(cli->outbuf); - memcpy(p, negTokenTarg.data, negTokenTarg.length); - p += negTokenTarg.length; + memcpy(p, blob.data, blob.length); + p += blob.length; p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE); p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE); cli_setup_bcc(cli, p); cli_send_smb(cli); if (!cli_receive_smb(cli)) - return False; + return blob2; show_msg(cli->inbuf); - if (cli_is_error(cli)) { - return False; + if (cli_is_error(cli) && !NT_STATUS_EQUAL(cli_nt_error(cli), + NT_STATUS_MORE_PROCESSING_REQUIRED)) { + return blob2; } /* use the returned vuid from now on */ @@ -404,17 +374,154 @@ static BOOL cli_session_setup_spnego(struct cli_state *cli, char *user, p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE); p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE); - fstrcpy(cli->user_name, user); + return blob2; +} - data_blob_free(negTokenTarg); - /* we don't need this blob until we do NTLMSSP */ +#if HAVE_KRB5 +/**************************************************************************** +do a spnego/kerberos encrypted session setup +****************************************************************************/ +static BOOL cli_session_setup_kerberos(struct cli_state *cli, char *principle, char *workgroup) +{ + DATA_BLOB blob2, negTokenTarg; + + /* generate the encapsulated kerberos5 ticket */ + negTokenTarg = spnego_gen_negTokenTarg(cli, principle); + + if (!negTokenTarg.data) return False; + + blob2 = cli_session_setup_blob(cli, negTokenTarg); + + /* we don't need this blob for kerberos */ data_blob_free(blob2); - return True; + return !cli_is_error(cli); } #endif +/**************************************************************************** +do a spnego/NTLMSSP encrypted session setup +****************************************************************************/ +static BOOL cli_session_setup_ntlmssp(struct cli_state *cli, char *user, + char *pass, char *workgroup) +{ + const char *mechs[] = {"1 3 6 1 4 1 311 2 2 10", NULL}; + DATA_BLOB msg1; + DATA_BLOB blob, chal1, chal2, auth; + uint8 challenge[8]; + uint8 nthash[24], lmhash[24], sess_key[16]; + uint32 neg_flags; + + neg_flags = NTLMSSP_NEGOTIATE_UNICODE | + NTLMSSP_NEGOTIATE_LM_KEY | + NTLMSSP_NEGOTIATE_NTLM; + + memset(sess_key, 0, 16); + + /* generate the ntlmssp negotiate packet */ + msrpc_gen(&blob, "CddB", + "NTLMSSP", + NTLMSSP_NEGOTIATE, + neg_flags, + sess_key, 16); + + /* and wrap it in a SPNEGO wrapper */ + msg1 = gen_negTokenTarg(mechs, blob); + data_blob_free(blob); + + /* now send that blob on its way */ + blob = cli_session_setup_blob(cli, msg1); + + data_blob_free(msg1); + + if (!NT_STATUS_EQUAL(cli_nt_error(cli), NT_STATUS_MORE_PROCESSING_REQUIRED)) { + return False; + } + + /* the server gives us back two challenges */ + if (!spnego_parse_challenge(blob, &chal1, &chal2)) { + return False; + } + + data_blob_free(blob); + + /* encrypt the password with the challenge */ + memcpy(challenge, chal1.data + 24, 8); + SMBencrypt(pass, challenge,lmhash); + SMBNTencrypt(pass, challenge,nthash); + + data_blob_free(chal1); + data_blob_free(chal2); + + /* this generates the actual auth packet */ + msrpc_gen(&blob, "CdBBUUUBd", + "NTLMSSP", + NTLMSSP_AUTH, + lmhash, 24, + nthash, 24, + workgroup, + user, + cli->calling.name, + sess_key, 16, + neg_flags); + + /* wrap it in SPNEGO */ + auth = spnego_gen_auth(blob); + + data_blob_free(blob); + + /* now send the auth packet and we should be done */ + blob = cli_session_setup_blob(cli, auth); + + data_blob_free(auth); + data_blob_free(blob); + + return !cli_is_error(cli); +} + + +/**************************************************************************** +do a spnego encrypted session setup +****************************************************************************/ +static BOOL cli_session_setup_spnego(struct cli_state *cli, char *user, + char *pass, char *workgroup) +{ + char *principle; + char *OIDs[ASN1_MAX_OIDS]; + uint8 guid[16]; + int i; + BOOL got_kerberos_mechanism = False; + + /* the server sent us the first part of the SPNEGO exchange in the negprot + reply */ + if (!spnego_parse_negTokenInit(cli->secblob, guid, OIDs, &principle)) { + return False; + } + + /* make sure the server understands kerberos */ + for (i=0;OIDs[i];i++) { + DEBUG(3,("got OID=%s\n", OIDs[i])); + if (strcmp(OIDs[i], "1 2 840 48018 1 2 2") == 0) { + got_kerberos_mechanism = True; + } + free(OIDs[i]); + } + DEBUG(3,("got principle=%s\n", principle)); + + fstrcpy(cli->user_name, user); + +#if HAVE_KRB5 + if (got_kerberos_mechanism && cli->use_kerberos) { + return cli_session_setup_kerberos(cli, principle, workgroup); + } +#endif + + free(principle); + + return cli_session_setup_ntlmssp(cli, user, pass, workgroup); +} + /**************************************************************************** Send a session setup. The username and workgroup is in UNIX character @@ -674,11 +781,6 @@ BOOL cli_negprot(struct cli_state *cli) CVAL(smb_buf(cli->outbuf),0) = 2; - if (cli->use_spnego) { - SSVAL(cli->outbuf, smb_flg2, - SVAL(cli->outbuf, smb_flg2) | FLAGS2_EXTENDED_SECURITY); - } - cli_send_smb(cli); if (!cli_receive_smb(cli)) return False; diff --git a/source3/libsmb/clientgen.c b/source3/libsmb/clientgen.c index b5eddd5644..25dc070024 100644 --- a/source3/libsmb/clientgen.c +++ b/source3/libsmb/clientgen.c @@ -112,10 +112,7 @@ void cli_setup_packet(struct cli_state *cli) if (cli->capabilities & CAP_STATUS32) { flags2 |= FLAGS2_32_BIT_ERROR_CODES; } - if (cli->use_spnego) { - /* once we have NTLMSSP we can enable this unconditionally */ - flags2 |= FLAGS2_EXTENDED_SECURITY; - } + flags2 |= FLAGS2_EXTENDED_SECURITY; SSVAL(cli->outbuf,smb_flg2, flags2); } } diff --git a/source3/libsmb/clikrb5.c b/source3/libsmb/clikrb5.c index b4847e4c2a..59a547b171 100644 --- a/source3/libsmb/clikrb5.c +++ b/source3/libsmb/clikrb5.c @@ -1,7 +1,7 @@ /* Unix SMB/Netbios implementation. Version 3.0 - simple kerberos5/SPNEGO routines + simple kerberos5 routines for active directory Copyright (C) Andrew Tridgell 2001 This program is free software; you can redistribute it and/or modify @@ -24,9 +24,6 @@ #if HAVE_KRB5 #include -#define OID_SPNEGO "1 3 6 1 5 5 2" -#define OID_KERBEROS5 "1 2 840 113554 1 2 2" - /* we can't use krb5_mk_req because w2k wants the service to be in a particular format */ @@ -86,7 +83,7 @@ cleanup_princ: /* get a kerberos5 ticket for the given service */ -static DATA_BLOB krb5_get_ticket(char *service, char *realm) +DATA_BLOB krb5_get_ticket(char *service, char *realm) { krb5_error_code retval; krb5_data packet; @@ -126,213 +123,11 @@ failed: } -/* - generate a negTokenInit packet given a GUID, a list of supported - OIDs (the mechanisms) and a principle name string -*/ -ASN1_DATA spnego_gen_negTokenInit(uint8 guid[16], - const char *OIDs[], - const char *principle) -{ - int i; - ASN1_DATA data; - - memset(&data, 0, sizeof(data)); - - asn1_write(&data, guid, 16); - asn1_push_tag(&data,ASN1_APPLICATION(0)); - asn1_write_OID(&data,OID_SPNEGO); - asn1_push_tag(&data,ASN1_CONTEXT(0)); - asn1_push_tag(&data,ASN1_SEQUENCE(0)); - - asn1_push_tag(&data,ASN1_CONTEXT(0)); - asn1_push_tag(&data,ASN1_SEQUENCE(0)); - for (i=0; OIDs[i]; i++) { - asn1_write_OID(&data,OIDs[i]); - } - asn1_pop_tag(&data); - asn1_pop_tag(&data); - - asn1_push_tag(&data, ASN1_CONTEXT(3)); - asn1_push_tag(&data, ASN1_SEQUENCE(0)); - asn1_push_tag(&data, ASN1_CONTEXT(0)); - asn1_write_GeneralString(&data,principle); - asn1_pop_tag(&data); - asn1_pop_tag(&data); - asn1_pop_tag(&data); - - asn1_pop_tag(&data); - asn1_pop_tag(&data); - - asn1_pop_tag(&data); - - if (data.has_error) { - DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs)); - asn1_free(&data); - } - - return data; -} - - -/* - parse a negTokenInit packet giving a GUID, a list of supported - OIDs (the mechanisms) and a principle name string -*/ -BOOL spnego_parse_negTokenInit(DATA_BLOB blob, - uint8 guid[16], - char *OIDs[ASN1_MAX_OIDS], - char **principle) -{ - int i; - BOOL ret; - ASN1_DATA data; - - asn1_load(&data, blob); - - asn1_read(&data, guid, 16); - asn1_start_tag(&data,ASN1_APPLICATION(0)); - asn1_check_OID(&data,OID_SPNEGO); - asn1_start_tag(&data,ASN1_CONTEXT(0)); - asn1_start_tag(&data,ASN1_SEQUENCE(0)); - - asn1_start_tag(&data,ASN1_CONTEXT(0)); - asn1_start_tag(&data,ASN1_SEQUENCE(0)); - for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) { - char *oid = NULL; - asn1_read_OID(&data,&oid); - OIDs[i] = oid; - } - OIDs[i] = NULL; - asn1_end_tag(&data); - asn1_end_tag(&data); - - asn1_start_tag(&data, ASN1_CONTEXT(3)); - asn1_start_tag(&data, ASN1_SEQUENCE(0)); - asn1_start_tag(&data, ASN1_CONTEXT(0)); - asn1_read_GeneralString(&data,principle); - asn1_end_tag(&data); - asn1_end_tag(&data); - asn1_end_tag(&data); - - asn1_end_tag(&data); - asn1_end_tag(&data); - - asn1_end_tag(&data); - - ret = !data.has_error; - asn1_free(&data); - return ret; -} - - -/* - generate a negTokenTarg packet given a list of OIDs and a security blob -*/ -static ASN1_DATA gen_negTokenTarg(const char *OIDs[], ASN1_DATA blob) -{ - int i; - ASN1_DATA data; - - memset(&data, 0, sizeof(data)); - - asn1_push_tag(&data, ASN1_APPLICATION(0)); - asn1_write_OID(&data,OID_SPNEGO); - asn1_push_tag(&data, ASN1_CONTEXT(0)); - asn1_push_tag(&data, ASN1_SEQUENCE(0)); - - asn1_push_tag(&data, ASN1_CONTEXT(0)); - asn1_push_tag(&data, ASN1_SEQUENCE(0)); - for (i=0; OIDs[i]; i++) { - asn1_write_OID(&data,OIDs[i]); - } - asn1_pop_tag(&data); - asn1_pop_tag(&data); - - asn1_push_tag(&data, ASN1_CONTEXT(2)); - asn1_write_OctetString(&data,blob.data,blob.length); - asn1_pop_tag(&data); - - asn1_pop_tag(&data); - asn1_pop_tag(&data); - - asn1_pop_tag(&data); - - if (data.has_error) { - DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data.ofs)); - asn1_free(&data); - } - - return data; -} - - -/* - generate a krb5 GSS-API wrapper packet given a ticket -*/ -static ASN1_DATA spnego_gen_krb5_wrap(DATA_BLOB ticket) -{ - ASN1_DATA data; - - memset(&data, 0, sizeof(data)); - - asn1_push_tag(&data, ASN1_APPLICATION(0)); - asn1_write_OID(&data, OID_KERBEROS5); - asn1_write_BOOLEAN(&data, 0); - asn1_write(&data, ticket.data, ticket.length); - asn1_pop_tag(&data); - - if (data.has_error) { - DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs)); - asn1_free(&data); - } - - return data; -} - - -/* - generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY - kerberos session setup -*/ -DATA_BLOB spnego_gen_negTokenTarg(struct cli_state *cli, char *principle) -{ - char *p; - fstring service; - char *realm; - DATA_BLOB tkt, ret; - ASN1_DATA tkt_wrapped, targ; - const char *krb_mechs[] = - {"1 2 840 48018 1 2 2", "1 3 6 1 4 1 311 2 2 10", NULL}; - - fstrcpy(service, principle); - p = strchr_m(service, '@'); - if (!p) { - DEBUG(1,("Malformed principle [%s] in spnego_gen_negTokenTarg\n", - principle)); - return data_blob(NULL, 0); - } - *p = 0; - realm = p+1; - - /* get a kerberos ticket for the service */ - tkt = krb5_get_ticket(service, realm); - - /* wrap that up in a nice GSS-API wrapping */ - tkt_wrapped = spnego_gen_krb5_wrap(tkt); - - /* and wrap that in a shiny SPNEGO wrapper */ - targ = gen_negTokenTarg(krb_mechs, tkt_wrapped); - - ret = data_blob(targ.data, targ.length); - - asn1_free(&tkt_wrapped); - asn1_free(&targ); - data_blob_free(tkt); - - return ret; -} - #else /* HAVE_KRB5 */ - void clikrb5_dummy(void) {} + /* this saves a few linking headaches */ + DATA_BLOB krb5_get_ticket(char *service, char *realm) + { + DEBUG(0,("NO KERBEROS SUPPORT\n")); + return data_blob(NULL, 0); + } #endif diff --git a/source3/libsmb/clispnego.c b/source3/libsmb/clispnego.c new file mode 100644 index 0000000000..6c6b18a923 --- /dev/null +++ b/source3/libsmb/clispnego.c @@ -0,0 +1,395 @@ +/* + Unix SMB/Netbios implementation. + Version 3.0 + simple kerberos5/SPNEGO routines + Copyright (C) Andrew Tridgell 2001 + + 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 02139, USA. +*/ + +#include "includes.h" + +#define OID_SPNEGO "1 3 6 1 5 5 2" +#define OID_KERBEROS5 "1 2 840 113554 1 2 2" + +/* + generate a negTokenInit packet given a GUID, a list of supported + OIDs (the mechanisms) and a principle name string +*/ +ASN1_DATA spnego_gen_negTokenInit(uint8 guid[16], + const char *OIDs[], + const char *principle) +{ + int i; + ASN1_DATA data; + + memset(&data, 0, sizeof(data)); + + asn1_write(&data, guid, 16); + asn1_push_tag(&data,ASN1_APPLICATION(0)); + asn1_write_OID(&data,OID_SPNEGO); + asn1_push_tag(&data,ASN1_CONTEXT(0)); + asn1_push_tag(&data,ASN1_SEQUENCE(0)); + + asn1_push_tag(&data,ASN1_CONTEXT(0)); + asn1_push_tag(&data,ASN1_SEQUENCE(0)); + for (i=0; OIDs[i]; i++) { + asn1_write_OID(&data,OIDs[i]); + } + asn1_pop_tag(&data); + asn1_pop_tag(&data); + + asn1_push_tag(&data, ASN1_CONTEXT(3)); + asn1_push_tag(&data, ASN1_SEQUENCE(0)); + asn1_push_tag(&data, ASN1_CONTEXT(0)); + asn1_write_GeneralString(&data,principle); + asn1_pop_tag(&data); + asn1_pop_tag(&data); + asn1_pop_tag(&data); + + asn1_pop_tag(&data); + asn1_pop_tag(&data); + + asn1_pop_tag(&data); + + if (data.has_error) { + DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs)); + asn1_free(&data); + } + + return data; +} + + +/* + parse a negTokenInit packet giving a GUID, a list of supported + OIDs (the mechanisms) and a principle name string +*/ +BOOL spnego_parse_negTokenInit(DATA_BLOB blob, + uint8 guid[16], + char *OIDs[ASN1_MAX_OIDS], + char **principle) +{ + int i; + BOOL ret; + ASN1_DATA data; + + asn1_load(&data, blob); + + asn1_read(&data, guid, 16); + asn1_start_tag(&data,ASN1_APPLICATION(0)); + asn1_check_OID(&data,OID_SPNEGO); + asn1_start_tag(&data,ASN1_CONTEXT(0)); + asn1_start_tag(&data,ASN1_SEQUENCE(0)); + + asn1_start_tag(&data,ASN1_CONTEXT(0)); + asn1_start_tag(&data,ASN1_SEQUENCE(0)); + for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) { + char *oid = NULL; + asn1_read_OID(&data,&oid); + OIDs[i] = oid; + } + OIDs[i] = NULL; + asn1_end_tag(&data); + asn1_end_tag(&data); + + asn1_start_tag(&data, ASN1_CONTEXT(3)); + asn1_start_tag(&data, ASN1_SEQUENCE(0)); + asn1_start_tag(&data, ASN1_CONTEXT(0)); + asn1_read_GeneralString(&data,principle); + asn1_end_tag(&data); + asn1_end_tag(&data); + asn1_end_tag(&data); + + asn1_end_tag(&data); + asn1_end_tag(&data); + + asn1_end_tag(&data); + + ret = !data.has_error; + asn1_free(&data); + return ret; +} + + +/* + generate a negTokenTarg packet given a list of OIDs and a security blob +*/ +DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob) +{ + int i; + ASN1_DATA data; + DATA_BLOB ret; + + memset(&data, 0, sizeof(data)); + + asn1_push_tag(&data, ASN1_APPLICATION(0)); + asn1_write_OID(&data,OID_SPNEGO); + asn1_push_tag(&data, ASN1_CONTEXT(0)); + asn1_push_tag(&data, ASN1_SEQUENCE(0)); + + asn1_push_tag(&data, ASN1_CONTEXT(0)); + asn1_push_tag(&data, ASN1_SEQUENCE(0)); + for (i=0; OIDs[i]; i++) { + asn1_write_OID(&data,OIDs[i]); + } + asn1_pop_tag(&data); + asn1_pop_tag(&data); + + asn1_push_tag(&data, ASN1_CONTEXT(2)); + asn1_write_OctetString(&data,blob.data,blob.length); + asn1_pop_tag(&data); + + asn1_pop_tag(&data); + asn1_pop_tag(&data); + + asn1_pop_tag(&data); + + if (data.has_error) { + DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data.ofs)); + asn1_free(&data); + } + + ret = data_blob(data.data, data.length); + asn1_free(&data); + + return ret; +} + + +/* + generate a krb5 GSS-API wrapper packet given a ticket +*/ +static DATA_BLOB spnego_gen_krb5_wrap(DATA_BLOB ticket) +{ + ASN1_DATA data; + DATA_BLOB ret; + + memset(&data, 0, sizeof(data)); + + asn1_push_tag(&data, ASN1_APPLICATION(0)); + asn1_write_OID(&data, OID_KERBEROS5); + asn1_write_BOOLEAN(&data, 0); + asn1_write(&data, ticket.data, ticket.length); + asn1_pop_tag(&data); + + if (data.has_error) { + DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs)); + asn1_free(&data); + } + + ret = data_blob(data.data, data.length); + asn1_free(&data); + + return ret; +} + + +/* + generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY + kerberos session setup +*/ +DATA_BLOB spnego_gen_negTokenTarg(struct cli_state *cli, char *principle) +{ + char *p; + fstring service; + char *realm; + DATA_BLOB tkt, tkt_wrapped, targ; + const char *krb_mechs[] = + {"1 2 840 48018 1 2 2", "1 3 6 1 4 1 311 2 2 10", NULL}; + + fstrcpy(service, principle); + p = strchr_m(service, '@'); + if (!p) { + DEBUG(1,("Malformed principle [%s] in spnego_gen_negTokenTarg\n", + principle)); + return data_blob(NULL, 0); + } + *p = 0; + realm = p+1; + + /* get a kerberos ticket for the service */ + tkt = krb5_get_ticket(service, realm); + + /* wrap that up in a nice GSS-API wrapping */ + tkt_wrapped = spnego_gen_krb5_wrap(tkt); + + /* and wrap that in a shiny SPNEGO wrapper */ + targ = gen_negTokenTarg(krb_mechs, tkt_wrapped); + + data_blob_free(tkt_wrapped); + data_blob_free(tkt); + + return targ; +} + + +/* + parse a spnego NTLMSSP challenge packet giving two security blobs +*/ +BOOL spnego_parse_challenge(DATA_BLOB blob, + DATA_BLOB *chal1, DATA_BLOB *chal2) +{ + BOOL ret; + ASN1_DATA data; + + asn1_load(&data, blob); + asn1_start_tag(&data,ASN1_CONTEXT(1)); + asn1_start_tag(&data,ASN1_SEQUENCE(0)); + + asn1_start_tag(&data,ASN1_CONTEXT(0)); + asn1_check_enumerated(&data,1); + asn1_end_tag(&data); + + asn1_start_tag(&data,ASN1_CONTEXT(1)); + asn1_check_OID(&data, "1 3 6 1 4 1 311 2 2 10"); + asn1_end_tag(&data); + + asn1_start_tag(&data,ASN1_CONTEXT(2)); + asn1_read_octet_string(&data, chal1); + asn1_end_tag(&data); + + asn1_start_tag(&data,ASN1_CONTEXT(3)); + asn1_read_octet_string(&data, chal2); + asn1_end_tag(&data); + + asn1_end_tag(&data); + asn1_end_tag(&data); + + ret = !data.has_error; + asn1_free(&data); + return ret; +} + +/* + generate a SPNEGO NTLMSSP auth packet. This will contain the encrypted passwords +*/ +DATA_BLOB spnego_gen_auth(DATA_BLOB blob) +{ + ASN1_DATA data; + DATA_BLOB ret; + + memset(&data, 0, sizeof(data)); + + asn1_push_tag(&data, ASN1_CONTEXT(1)); + asn1_push_tag(&data, ASN1_SEQUENCE(0)); + asn1_push_tag(&data, ASN1_CONTEXT(2)); + asn1_write_OctetString(&data,blob.data,blob.length); + asn1_pop_tag(&data); + asn1_pop_tag(&data); + asn1_pop_tag(&data); + + ret = data_blob(data.data, data.length); + + asn1_free(&data); + + return ret; + +} + + +/* + this is a tiny msrpc packet generator. I am only using this to + avoid tying this code to a particular varient of our rpc code. This + generator is not general enough for all our rpc needs, its just + enough for the spnego/ntlmssp code + + format specifiers are: + + U = unicode string (input is unix string) + B = data blob (pointer + length) + d = word (4 bytes) + C = constant ascii string + */ +BOOL msrpc_gen(DATA_BLOB *blob, + const char *format, ...) +{ + int i, n; + va_list ap; + char *s; + uint8 *b; + int head_size=0, data_size=0; + int head_ofs, data_ofs; + + /* first scan the format to work out the header and body size */ + va_start(ap, format); + for (i=0; format[i]; i++) { + switch (format[i]) { + case 'U': + s = va_arg(ap, char *); + head_size += 8; + data_size += str_charnum(s) * 2; + break; + case 'B': + b = va_arg(ap, uint8 *); + head_size += 8; + data_size += va_arg(ap, int); + break; + case 'd': + n = va_arg(ap, int); + head_size += 4; + break; + case 'C': + s = va_arg(ap, char *); + head_size += str_charnum(s) + 1; + break; + } + } + va_end(ap); + + /* allocate the space, then scan the format again to fill in the values */ + blob->data = malloc(head_size + data_size); + blob->length = head_size + data_size; + if (!blob->data) return False; + + head_ofs = 0; + data_ofs = head_size; + + va_start(ap, format); + for (i=0; format[i]; i++) { + switch (format[i]) { + case 'U': + s = va_arg(ap, char *); + n = str_charnum(s); + SSVAL(blob->data, head_ofs, n*2); head_ofs += 2; + SSVAL(blob->data, head_ofs, n*2); head_ofs += 2; + SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4; + push_string(NULL, blob->data+data_ofs, s, n*2, STR_UNICODE|STR_NOALIGN); + data_ofs += n*2; + break; + case 'B': + b = va_arg(ap, uint8 *); + n = va_arg(ap, int); + SSVAL(blob->data, head_ofs, n); head_ofs += 2; + SSVAL(blob->data, head_ofs, n); head_ofs += 2; + SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4; + memcpy(blob->data+data_ofs, b, n); + data_ofs += n; + break; + case 'd': + n = va_arg(ap, int); + SIVAL(blob->data, head_ofs, n); head_ofs += 4; + break; + case 'C': + s = va_arg(ap, char *); + head_ofs += push_string(NULL, blob->data+head_ofs, s, -1, + STR_ASCII|STR_TERMINATE); + break; + } + } + va_end(ap); + + return True; +} -- cgit