NTLMSSP parinoia - we really don't want to run over the end of our blob,
[jra/samba/.git] / source3 / libsmb / clispnego.c
index 8376398e3fa53dafbb5bea3c58d2381ef4fe1b9d..54069fc63745089ba4e00147829b29a0b6547fa1 100644 (file)
@@ -73,6 +73,50 @@ DATA_BLOB spnego_gen_negTokenInit(uint8 guid[16],
        return ret;
 }
 
+/*
+  Generate a negTokenInit as used by the client side ... It has a mechType
+  (OID), and a mechToken (a security blob) ... 
+
+  Really, we need to break out the NTLMSSP stuff as well, because it could be
+  raw in the packets!
+*/
+DATA_BLOB gen_negTokenInit(const char *OID, DATA_BLOB blob)
+{
+       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));
+       asn1_write_OID(&data, OID);
+       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 negTokenInit at offset %d\n", (int)data.ofs));
+               asn1_free(&data);
+       }
+
+       ret = data_blob(data.data, data.length);
+       asn1_free(&data);
+
+       return ret;
+}
 
 /*
   parse a negTokenInit packet giving a GUID, a list of supported
@@ -277,13 +321,13 @@ BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket)
    generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
    kerberos session setup 
 */
-DATA_BLOB spnego_gen_negTokenTarg(const char *principal)
+DATA_BLOB spnego_gen_negTokenTarg(const char *principal, int time_offset)
 {
        DATA_BLOB tkt, tkt_wrapped, targ;
        const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL};
 
        /* get a kerberos ticket for the service */
-       tkt = krb5_get_ticket(principal);
+       tkt = krb5_get_ticket(principal, time_offset);
 
        /* wrap that up in a nice GSS-API wrapping */
        tkt_wrapped = spnego_gen_krb5_wrap(tkt);
@@ -342,51 +386,6 @@ BOOL spnego_parse_challenge(DATA_BLOB blob,
 }
 
 
-/*
-  generate a spnego NTLMSSP challenge packet given two security blobs
-  The second challenge is optional
-*/
-BOOL spnego_gen_challenge(DATA_BLOB *blob,
-                         DATA_BLOB *chal1, DATA_BLOB *chal2)
-{
-       ASN1_DATA data;
-
-       ZERO_STRUCT(data);
-
-       asn1_push_tag(&data,ASN1_CONTEXT(1));
-       asn1_push_tag(&data,ASN1_SEQUENCE(0));
-
-       asn1_push_tag(&data,ASN1_CONTEXT(0));
-       asn1_write_enumerated(&data,1);
-       asn1_pop_tag(&data);
-
-       asn1_push_tag(&data,ASN1_CONTEXT(1));
-       asn1_write_OID(&data, OID_NTLMSSP);
-       asn1_pop_tag(&data);
-
-       asn1_push_tag(&data,ASN1_CONTEXT(2));
-       asn1_write_OctetString(&data, chal1->data, chal1->length);
-       asn1_pop_tag(&data);
-
-       /* the second challenge is optional (XP doesn't send it) */
-       if (chal2) {
-               asn1_push_tag(&data,ASN1_CONTEXT(3));
-               asn1_write_OctetString(&data, chal2->data, chal2->length);
-               asn1_pop_tag(&data);
-       }
-
-       asn1_pop_tag(&data);
-       asn1_pop_tag(&data);
-
-       if (data.has_error) {
-               return False;
-       }
-
-       *blob = data_blob(data.data, data.length);
-       asn1_free(&data);
-       return True;
-}
-
 /*
  generate a SPNEGO NTLMSSP auth packet. This will contain the encrypted passwords
 */
@@ -441,18 +440,37 @@ BOOL spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
 /*
   generate a minimal SPNEGO NTLMSSP response packet.  Doesn't contain much.
 */
-DATA_BLOB spnego_gen_auth_response(void)
+DATA_BLOB spnego_gen_auth_response(DATA_BLOB *ntlmssp_reply, NTSTATUS nt_status)
 {
        ASN1_DATA data;
        DATA_BLOB ret;
+       uint8 negResult;
 
-       memset(&data, 0, sizeof(data));
+       if (NT_STATUS_IS_OK(nt_status)) {
+               negResult = SPNEGO_NEG_RESULT_ACCEPT;
+       } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+               negResult = SPNEGO_NEG_RESULT_INCOMPLETE; 
+       } else {
+               negResult = SPNEGO_NEG_RESULT_REJECT; 
+       }
+
+       ZERO_STRUCT(data);
 
        asn1_push_tag(&data, ASN1_CONTEXT(1));
        asn1_push_tag(&data, ASN1_SEQUENCE(0));
        asn1_push_tag(&data, ASN1_CONTEXT(0));
-       asn1_write_enumerated(&data, 0);        
+       asn1_write_enumerated(&data, negResult);
        asn1_pop_tag(&data);
+       if (negResult == SPNEGO_NEG_RESULT_INCOMPLETE) {
+               asn1_push_tag(&data,ASN1_CONTEXT(1));
+               asn1_write_OID(&data, OID_NTLMSSP);
+               asn1_pop_tag(&data);
+               
+               asn1_push_tag(&data,ASN1_CONTEXT(2));
+               asn1_write_OctetString(&data, ntlmssp_reply->data, ntlmssp_reply->length);
+               asn1_pop_tag(&data);
+       }
+
        asn1_pop_tag(&data);
        asn1_pop_tag(&data);
 
@@ -470,10 +488,12 @@ DATA_BLOB spnego_gen_auth_response(void)
   format specifiers are:
 
   U = unicode string (input is unix string)
-  a = address (1 byte type, 1 byte length, unicode string, all inline)
-  A = ASCII string (pointer + length) Actually same as B
+  a = address (input is BOOL unicode, char *unix_string)
+      (1 byte type, 1 byte length, unicode/ASCII string, all inline)
+  A = ASCII string (input is unix string)
   B = data blob (pointer + length)
   b = data blob in header (pointer + length)
+  D
   d = word (4 bytes)
   C = constant ascii string
  */
@@ -486,6 +506,7 @@ BOOL msrpc_gen(DATA_BLOB *blob,
        uint8 *b;
        int head_size=0, data_size=0;
        int head_ofs, data_ofs;
+       BOOL unicode;
 
        /* first scan the format to work out the header and body size */
        va_start(ap, format);
@@ -496,12 +517,21 @@ BOOL msrpc_gen(DATA_BLOB *blob,
                        head_size += 8;
                        data_size += str_charnum(s) * 2;
                        break;
+               case 'A':
+                       s = va_arg(ap, char *);
+                       head_size += 8;
+                       data_size += str_ascii_charnum(s);
+                       break;
                case 'a':
+                       unicode = va_arg(ap, BOOL);
                        n = va_arg(ap, int);
                        s = va_arg(ap, char *);
-                       data_size += (str_charnum(s) * 2) + 4;
+                       if (unicode) {
+                               data_size += (str_charnum(s) * 2) + 4;
+                       } else {
+                               data_size += (str_ascii_charnum(s)) + 4;
+                       }
                        break;
-               case 'A':
                case 'B':
                        b = va_arg(ap, uint8 *);
                        head_size += 8;
@@ -541,19 +571,39 @@ BOOL msrpc_gen(DATA_BLOB *blob,
                        push_string(NULL, blob->data+data_ofs, s, n*2, STR_UNICODE|STR_NOALIGN);
                        data_ofs += n*2;
                        break;
+               case 'A':
+                       s = va_arg(ap, char *);
+                       n = str_ascii_charnum(s);
+                       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;
+                       push_string(NULL, blob->data+data_ofs, s, n, STR_ASCII|STR_NOALIGN);
+                       data_ofs += n;
+                       break;
                case 'a':
+                       unicode = va_arg(ap, BOOL);
                        n = va_arg(ap, int);
                        SSVAL(blob->data, data_ofs, n); data_ofs += 2;
                        s = va_arg(ap, char *);
-                       n = str_charnum(s);
-                       SSVAL(blob->data, data_ofs, n*2); data_ofs += 2;
-                       if (0 < n) {
-                               push_string(NULL, blob->data+data_ofs, s, n*2,
-                                           STR_UNICODE|STR_NOALIGN);
+                       if (unicode) {
+                               n = str_charnum(s);
+                               SSVAL(blob->data, data_ofs, n*2); data_ofs += 2;
+                               if (0 < n) {
+                                       push_string(NULL, blob->data+data_ofs, s, n*2,
+                                                   STR_UNICODE|STR_NOALIGN);
+                               }
+                               data_ofs += n*2;
+                       } else {
+                               n = str_ascii_charnum(s);
+                               SSVAL(blob->data, data_ofs, n); data_ofs += 2;
+                               if (0 < n) {
+                                       push_string(NULL, blob->data+data_ofs, s, n,
+                                                   STR_ASCII|STR_NOALIGN);
+                               }
+                               data_ofs += n;
                        }
-                       data_ofs += n*2;
                        break;
-                       
+
                case 'B':
                        b = va_arg(ap, uint8 *);
                        n = va_arg(ap, int);
@@ -586,6 +636,12 @@ BOOL msrpc_gen(DATA_BLOB *blob,
 }
 
 
+/* a helpful macro to avoid running over the end of our blob */
+#define NEED_DATA(amount) \
+if (head_ofs + amount > blob->length) { \
+        return False; \
+}
+
 /*
   this is a tiny msrpc packet parser. This the the partner of msrpc_gen
 
@@ -598,6 +654,7 @@ BOOL msrpc_gen(DATA_BLOB *blob,
   d = word (4 bytes)
   C = constant ascii string
  */
+
 BOOL msrpc_parse(DATA_BLOB *blob,
                 const char *format, ...)
 {
@@ -615,19 +672,32 @@ BOOL msrpc_parse(DATA_BLOB *blob,
        for (i=0; format[i]; i++) {
                switch (format[i]) {
                case 'U':
+                       NEED_DATA(8);
                        len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
                        len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
                        ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
+
                        /* make sure its in the right format - be strict */
-                       if (len1 != len2 || (len1&1) || ptr + len1 > blob->length) {
+                       if (len1 != len2 || ptr + len1 > blob->length) {
                                return False;
                        }
+                       if (len1 & 1) {
+                               /* if odd length and unicode */
+                               return False;
+                       }
+
                        ps = va_arg(ap, char **);
-                       pull_string(NULL, p, blob->data + ptr, -1, len1, 
-                                   STR_UNICODE|STR_NOALIGN);
-                       (*ps) = strdup(p);
+                       if (0 < len1) {
+                               pull_string(NULL, p, blob->data + ptr, sizeof(p), 
+                                           len1, 
+                                           STR_UNICODE|STR_NOALIGN);
+                               (*ps) = strdup(p);
+                       } else {
+                               (*ps) = NULL;
+                       }
                        break;
                case 'A':
+                       NEED_DATA(8);
                        len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
                        len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
                        ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
@@ -636,16 +706,19 @@ BOOL msrpc_parse(DATA_BLOB *blob,
                        if (len1 != len2 || ptr + len1 > blob->length) {
                                return False;
                        }
+
                        ps = va_arg(ap, char **);
                        if (0 < len1) {
-                               pull_string(NULL, p, blob->data + ptr, -1, 
-                                           len1, STR_ASCII|STR_NOALIGN);
+                               pull_string(NULL, p, blob->data + ptr, sizeof(p), 
+                                           len1, 
+                                           STR_ASCII|STR_NOALIGN);
                                (*ps) = strdup(p);
                        } else {
                                (*ps) = NULL;
                        }
                        break;
                case 'B':
+                       NEED_DATA(8);
                        len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
                        len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
                        ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
@@ -659,16 +732,19 @@ BOOL msrpc_parse(DATA_BLOB *blob,
                case 'b':
                        b = (DATA_BLOB *)va_arg(ap, void *);
                        len1 = va_arg(ap, unsigned);
+                       /* make sure its in the right format - be strict */
+                       NEED_DATA(len1);
                        *b = data_blob(blob->data + head_ofs, len1);
                        head_ofs += len1;
                        break;
                case 'd':
                        v = va_arg(ap, uint32 *);
+                       NEED_DATA(4);
                        *v = IVAL(blob->data, head_ofs); head_ofs += 4;
                        break;
                case 'C':
                        s = va_arg(ap, char *);
-                       head_ofs += pull_string(NULL, p, blob->data+head_ofs, -1
+                       head_ofs += pull_string(NULL, p, blob->data+head_ofs, sizeof(p)
                                                blob->length - head_ofs, 
                                                STR_ASCII|STR_TERMINATE);
                        if (strcmp(s, p) != 0) {