added NTLMSSP authentication to libsmb. It seems to work well so I have enabled it...
authorAndrew Tridgell <tridge@samba.org>
Fri, 12 Oct 2001 04:49:42 +0000 (04:49 +0000)
committerAndrew Tridgell <tridge@samba.org>
Fri, 12 Oct 2001 04:49:42 +0000 (04:49 +0000)
source/Makefile.in
source/client/client.c
source/include/asn1.h
source/include/client.h
source/libsmb/asn1.c
source/libsmb/cliconnect.c
source/libsmb/clientgen.c
source/libsmb/clikrb5.c
source/libsmb/clispnego.c [new file with mode: 0644]

index 30c4cbb44e210d70d4bfed912ea51fe05dd46ba9..b770945d1738acb4db1b5c2ec27ae4fd0c42f5cb 100644 (file)
@@ -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 \
index dfcb3d9616c80d0f452a5619879860a0d135450c..1317346df3d8fbb32eb3b1ed4237f9aaf5bc7e90 100644 (file)
@@ -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;
index 183a9ced6c52896dbd1d4987d60bee18234c2740..8d9bb158c8045f68de773b172ad848dc33a858ca 100644 (file)
@@ -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
index 24a9d47090254f8841ce5e229b3c92bab4ea1658..22ea10b9a785f3438095a4413a77157fdd316fd8 100644 (file)
@@ -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? */
index 17b1ee1089f5b4cbd40a79322dd766b441e7fca8..6a92a6be0048dd81a00c0d76d2e7aed84fe95051 100644 (file)
@@ -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);
+}
index 36aedf2d59170fe1c64ab72c5bfdba3f1d034e59..11852a09cc895ace3afdd4ff80035a7a19edb97f 100644 (file)
@@ -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;
index b5eddd5644baa9dca440f1adc979343f846de7ef..25dc070024a1ed6bdaedda59cb47de1abaa735ce 100644 (file)
@@ -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);
        }
 }
index b4847e4c2aa09d0eb3d680937d9587f8ce2acc2b..59a547b171e0397059ff3d5322c3d03206895705 100644 (file)
@@ -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/source/libsmb/clispnego.c b/source/libsmb/clispnego.c
new file mode 100644 (file)
index 0000000..6c6b18a
--- /dev/null
@@ -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;
+}