summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2001-10-12 04:49:42 +0000
committerAndrew Tridgell <tridge@samba.org>2001-10-12 04:49:42 +0000
commit9f7cb41f11c0d2fc09104f6998f75c59bc363b26 (patch)
treeef023f03e5ea53a178c2d874a682f607e13f6397
parent8632b44f6ba2a1b8698c62778dc6547bed4bae92 (diff)
downloadsamba-9f7cb41f11c0d2fc09104f6998f75c59bc363b26.tar.gz
samba-9f7cb41f11c0d2fc09104f6998f75c59bc363b26.tar.bz2
samba-9f7cb41f11c0d2fc09104f6998f75c59bc363b26.zip
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)
-rw-r--r--source3/Makefile.in3
-rw-r--r--source3/client/client.c5
-rw-r--r--source3/include/asn1.h1
-rw-r--r--source3/include/client.h2
-rw-r--r--source3/libsmb/asn1.c27
-rw-r--r--source3/libsmb/cliconnect.c206
-rw-r--r--source3/libsmb/clientgen.c5
-rw-r--r--source3/libsmb/clikrb5.c221
-rw-r--r--source3/libsmb/clispnego.c395
9 files changed, 590 insertions, 275 deletions
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 <krb5.h>
-#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;
+}