/* detection functions */
int rule13308eval(void *p) {
    const u_int8_t *cursor_normal = 0;
    const u_int8_t *beg_of_buffer, *end_of_buffer;

    SFSnortPacket *sp = (SFSnortPacket *) p;
    
    // Base64 stuff
    u_int8_t base64buf[256], decodedbuf[256];
    u_int32_t inputchars, base64bytes, decodedbytes;

    int i;

    if(sp == NULL)
        return RULE_NOMATCH;

    // flow:established, to_server;
    if (checkFlow(p, rule13308options[0]->option_u.flowFlags) <= 0)
        return RULE_NOMATCH;

    // Doing this content match is pretty useless because it's duplicated in our PCRE.
    // But we want to keep the structure for the pattern matcher.
//    // content:"Authorization|3A|", depth 0, nocase, fast_pattern;
//    if (contentMatch(p, rule13308options[1]->option_u.content, &cursor_normal) <= 0)
//        return RULE_NOMATCH;

    // pcre:"^Authorization\x3A\s*Basic[ \t]+", dotall, multiline, nocase;
    if (pcreMatch(p, rule13308options[2]->option_u.pcre, &cursor_normal) <= 0)
        return RULE_NOMATCH;

    if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_buffer, &end_of_buffer) != CURSOR_IN_BOUNDS)
        return RULE_NOMATCH;

    // At this point, cursor should point to the start of the auth data
    inputchars = (end_of_buffer > cursor_normal + sizeof(base64buf)) ? sizeof(base64buf) : end_of_buffer - cursor_normal;

    DEBUG_SO(printf("%d input chars: %*s\n", inputchars, inputchars, cursor_normal));

    if(unfold_header(cursor_normal, inputchars, base64buf, sizeof(base64buf), &base64bytes) != 0)
        return RULE_NOMATCH;

    DEBUG_SO(printf("Successfully unfolded header (%s)(%d)\n", base64buf, base64bytes));

    if(base64decode(base64buf, base64bytes, decodedbuf, sizeof(decodedbuf), &decodedbytes) < 0)
        return RULE_NOMATCH;

    DEBUG_SO(printf("Successfully base64 decoded (%s)(%d)\n", decodedbuf, decodedbytes));
  
    for(i=0; i<decodedbytes; i++) {
        DEBUG_SO(printf("checking byte: %c\n", decodedbuf[i]));
        if(decodedbuf[i] == '%') {
            return RULE_MATCH;
        } else if(decodedbuf[i] == ':') {
            // Separator between username:password
            return RULE_NOMATCH;
        }
    }        
        
    return RULE_NOMATCH;
}
/* detection functions */
int rule17741eval(void *p)
{
    const uint8_t *cursor_normal = 0, *end_of_payload;    
    SFSnortPacket *sp = (SFSnortPacket *) p;

    BER_ELEMENT ber_element;
    BER_ELEMENT req_body;
    BER_ELEMENT req_body_SEQUENCE;       
    int         ret;

    DEBUG_SO(printf("rule17741eval enter\n"));

    if(sp == NULL)
       return RULE_NOMATCH;

    // flow:to_server;
    if(checkFlow(p, rule17741options[0]->option_u.flowFlags) <= 0 )
        return RULE_NOMATCH;
        
    if (getBuffer(sp, CONTENT_BUF_NORMALIZED, &cursor_normal, &end_of_payload) <= 0)
        return RULE_NOMATCH;
  
    ret = ber_get_element(sp, cursor_normal, &ber_element);

    DEBUG_SO(printf("First element type 0x%02x\n", ber_element.type));

    if(ret < 0 || ((ber_element.type != 0x6a) && (ber_element.type != 0x6c)))
       return RULE_NOMATCH;

    // We want to delve into this element
    cursor_normal = ber_element.data.data_ptr;
 
    BER_DATA(0x30);         // SEQUENCE
    BER_SKIP(0xA1);         // pvno [1] INTEGER (5)
    BER_SKIP(0xA2);         // msg-type [2] INTEGER (10 -- AS -- | 12 -- TGS --)
    
    // if optional PA-DATA exists, skip it
    // 10 1 00011  (context-specific, structured, tag number 3)
    if (cursor_normal < end_of_payload && *cursor_normal == 0xA3)
        BER_SKIP(0xA3);     // padata [3] SEQUENCE OF PA-DATA OPTIONAL
   
    // req-body [4] KDC-REQ-BODY
    // req-body is defined as SEQUENCE and if req-body's data size is not equal to 
    // the total size of SEQUENCE, it is malicious
    
    ret = ber_get_element(sp, cursor_normal, &req_body);
    if (ret < 0)
        return RULE_NOMATCH;
    
    BER_DATA(0xA4);         

    ret = ber_get_element(sp, cursor_normal, &req_body_SEQUENCE);
    if (ret < 0)
        return RULE_NOMATCH;
 
    DEBUG_SO(printf("req_body.data_len = 0x%08x, req_body_SEQUENCE.specified_total_len = 0x%08x\n", req_body.data_len, req_body_SEQUENCE.specified_total_len);)
        
    if (req_body.data_len != req_body_SEQUENCE.specified_total_len)
/* detection functions */
int rule17697eval(void *p) {
    const uint8_t *cursor_normal = 0;
    const uint8_t *beg_of_buffer, *end_of_buffer;

    uint8_t decodedbuf[MAX_BASE64_BUFFER_SIZE], *decodedbuf_ptr;
    uint32_t inputchars, decodedbytes;
    SFSnortPacket *sp = (SFSnortPacket *) p;

    uint32_t tmpval = 0;

    if(sp == NULL)
        return RULE_NOMATCH;

    if(sp->payload == NULL)
        return RULE_NOMATCH;
    
    // flow:established, to_server;
    if (checkFlow(p, rule17697options[0]->option_u.flowFlags) > 0 ) {
        // content:"-----BEGIN PGP MESSAGE-----", depth 0, nocase, fast_pattern;
        if (contentMatch(p, rule17697options[1]->option_u.content, &cursor_normal) > 0) {
            DEBUG_SO(printf("Matched the PGP header\n"));

            // content:"Version|3A|", offset 2, depth 8, nocase, relative;
            if (contentMatch(p, rule17697options[2]->option_u.content, &cursor_normal) > 0) {
                DEBUG_SO(printf("Matched the version\n"));

                // content:"|0D 0A 0D 0A|", depth 0, relative;
                if (contentMatch(p, rule17697options[3]->option_u.content, &cursor_normal) > 0) {
                    DEBUG_SO(printf("Matched the newline\n"));

                    if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_buffer, &end_of_buffer) != CURSOR_IN_BOUNDS)
                        return RULE_NOMATCH;
               
                    // We should now be at the beginning of the PGP data
                    // Four base64 input chars become three output chars
                    inputchars = (end_of_buffer > cursor_normal + (sizeof(decodedbuf) * 4 / 3)) ? (sizeof(decodedbuf) * 4 / 3) : end_of_buffer - cursor_normal;
                    DEBUG_SO(printf("Decoding %d bytes\n", inputchars));

                    // Only need 6 output bytes, plus 1 byte for the NULL added by base64decode()
                    if(base64decode(cursor_normal, inputchars, decodedbuf, 7, &decodedbytes) < 0) {
                        DEBUG_SO(printf("Failed to decode any data to work with\n"));
                        return RULE_NOMATCH;
                    }
                    
                    DEBUG_SO(printf("Decoded %d bytes\n", decodedbytes));

                    // Make sure we have enough data to work with
                    if(decodedbytes >= 6) {
                        decodedbuf_ptr = decodedbuf;

                        // New format with content tag of 16 or 61 (both in the first byte)
                        DEBUG_SO(printf("Packet format: %01x\n", decodedbuf[0]));

                        // The top two bits are set, the lower six we want to be either 16 or 61.
                        // 0xC0 + 16 = 0xD0,  0xC0 + 61 = 0xFD
                        if((decodedbuf[0] == (uint8_t)0xD0) || (decodedbuf[0] == (uint8_t)0xFD)) {

                            if(decodedbuf[1] == 0xFF) {
                               decodedbuf_ptr = decodedbuf + 2;

                               tmpval =  *decodedbuf_ptr++;
                               tmpval |= *decodedbuf_ptr++ << 8;
                               tmpval |= *decodedbuf_ptr++ << 16;
                               tmpval |= *decodedbuf_ptr++ << 24;

                               DEBUG_SO(printf("Packet Size: 0x%08x\n", tmpval));

                               if((tmpval >= 0xF9FFFFFF) && (tmpval <= 0xFEFFFFFF)) {
                                  return RULE_MATCH;
                               }
                            }
                        }       
                    }
                }
            }
        }
    }
    return RULE_NOMATCH;
}
/* detection functions */
int rule16394eval(void *p) {
   const uint8_t *cursor_normal = 0;
   SFSnortPacket *sp = (SFSnortPacket *) p;

   const uint8_t *cursor_padata;

   BER_ELEMENT ber_element;
   int retval;

   uint32_t renew_realm_len;
   const uint8_t *renew_realm_str;

   uint32_t ticket_realm_len;
   const uint8_t *ticket_realm_str;

   DEBUG_SO(int i);

   DEBUG_SO(printf("rule16394eval enter\n"));

   if(sp == NULL)
      return RULE_NOMATCH;

   if(sp->payload == NULL)
      return RULE_NOMATCH;
   
   // flow:established, to_server;
   if(checkFlow(p, rule16394options[0]->option_u.flowFlags) <= 0)
      return RULE_NOMATCH;

   // For speed and for ease of programming, we take advantage of some content matches
   // here in some places at the expense of false negatives.
   // But they do also to give us a nice content match.

   // This content match skips over a few items then matches on Kerberos protocol
   // version (pvno) 5 and mesg type TGS-REQ (12)
   // content:"|A1 03 02 01 05 A2 03 02 01 0C|", payload raw, depth 22, fast_pattern;
   if(contentMatch(p, rule16394options[1]->option_u.content, &cursor_normal) <= 0)
      return RULE_NOMATCH;

   // Save the pointer so we can come back here, and we're going to jump ahead to make
   // sure this is a renew packet as well as store the pointer to and size of the
   // renewal realm
   cursor_padata = cursor_normal;

   BER_SKIP(0xa3); // This is a wrapper to ber_skip_element() that NOMATCH's on failure

   // We should now be at the start of the KDC_REQ_BODY

   BER_DATA(0xa4); // This is a wrapper to ber_point_to_data() that NOMATCH's on failure
   BER_DATA(0x30); 

   // We're going to cheat again here and do a quick content match.
   // Plus, this is our fast pattern match.
   // content:"|A0 07 03 05 00 00 00 00 02|", payload raw, depth 9, relative, fast_pattern;
   if(contentMatch(p, rule16394options[2]->option_u.content, &cursor_normal) <= 0)
      return RULE_NOMATCH;

   BER_DATA(0xa2);
 
   // Grab the piece of data we've been looking for
   retval = ber_get_element(sp, cursor_normal, &ber_element);

   // Type 0x1b is essentially a string.  Don't know why it's not type 0x04. 
   // Negative return value means error.  0 means 0 data bytes and therefore useless.
   // Also make sure there is the full amount of data present.
   if((retval <= 0) || (ber_element.type != 0x1b) || (retval < ber_element.data_len))
      return RULE_NOMATCH;

   renew_realm_len = ber_element.data_len;
   renew_realm_str = ber_element.data.data_ptr;

   DEBUG_SO(for(i=0; i<renew_realm_len; i++) printf("%c", renew_realm_str[i]); printf("\n"));

   // Now that we have our renew_realm info and we've verified the data is complete,
   // Let's go back to the beginning and get our ticket_realm information
   cursor_normal = cursor_padata;

   // This is a very long list.  Glad we made sure we were in a renewal packet and
   // had all of the data we need before we bother with this.  :)
   BER_DATA(0xa3);
   BER_DATA(0x30);
   BER_DATA(0x30); 
   BER_SKIP(0xa1);
   BER_DATA(0xa2);
   BER_DATA(0x04);
   BER_DATA(0x6e);
   BER_DATA(0x30);
   BER_SKIP(0xa0);
   BER_SKIP(0xa1);
   BER_SKIP(0xa2);
   BER_DATA(0xa3);
   BER_DATA(0x61);
   BER_DATA(0x30);
   BER_SKIP(0xa0);
   BER_DATA(0xa1);

   // Same code and checks as above for our ticket/home realm
   retval = ber_get_element(sp, cursor_normal, &ber_element);
   if((retval <= 0) || (ber_element.type != 0x1b) || (retval < ber_element.data_len))
      return RULE_NOMATCH;

   ticket_realm_len = ber_element.data_len;
   ticket_realm_str = ber_element.data.data_ptr;

   DEBUG_SO(for(i=0; i<ticket_realm_len; i++) printf("%c", ticket_realm_str[i]); printf("\n"));

   // Match if the realm names are different (either lens are diff or value is diff)
   if(ticket_realm_len != renew_realm_len || memcmp(ticket_realm_str, renew_realm_str, ticket_realm_len))
      return RULE_MATCH;

   return RULE_NOMATCH;
}
/* detection functions */
int rule16375eval(void *p) {
   const u_int8_t *cursor_normal = 0, *beg_of_payload, *end_of_payload;
   SFSnortPacket *sp = (SFSnortPacket *) p;

   BER_ELEMENT ber_element;

   const u_int8_t *end_of_string;
   u_int32_t namelen = 0;

   if(sp == NULL)
      return RULE_NOMATCH;

   if(sp->payload == NULL)
      return RULE_NOMATCH;
   
   // flow:established, to_server;
   if(checkFlow(p, rule16375options[0]->option_u.flowFlags) <= 0)
      return RULE_NOMATCH;

   // Initialize our pointer
   if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0)
      return RULE_NOMATCH;

   // Universal Sequence
   if(ber_point_to_data(sp, &cursor_normal, 0x30) < 0)
      return RULE_NOMATCH;

   // Message ID
   if(ber_skip_element(sp, &cursor_normal, 0x02) < 0)
      return RULE_NOMATCH;

   // Search Request
   if(ber_point_to_data(sp, &cursor_normal, 0x66) < 0)
      return RULE_NOMATCH;

   // Object
   if(ber_get_element(sp, cursor_normal, &ber_element) < 0)
      return RULE_NOMATCH;

   // Make sure it's a string
   if(ber_element.type != 0x04)
      return RULE_NOMATCH;

   // Move the cursor to the start of the string data
   cursor_normal = ber_element.data.data_ptr;

   end_of_string = cursor_normal + ber_element.data_len;

   // Check for end of buffer
   if(end_of_string > end_of_payload)
      end_of_string = end_of_payload;

   // Now we have cursor_normal pointing to the start of the
   // string and end_of_string set appropriately.  Now,
   // let's see how long the parameter names are.
   while(cursor_normal < end_of_string) {
      if(*cursor_normal != '=')
         namelen++;
      else {
         DEBUG_SO(printf("rule16375: namelen=%d\n", namelen));

         if(namelen > 100)
            return RULE_MATCH;

         // Length is fine.  Set back to zero.
         namelen = 0;

         // Now jump over the value
         while((cursor_normal < end_of_string) && (*cursor_normal != ','))
            cursor_normal++;
      }

      cursor_normal++;
   }

   // If we were in the middle of an overly long parameter name
   // when we ran out of data, match.
   if(namelen > 100)
      return RULE_MATCH;

   return RULE_NOMATCH;
}
/* detection functions */
int rule16415eval(void *p) {
    const uint8_t *cursor_normal = 0;
    SFSnortPacket *sp = (SFSnortPacket *) p;

    const uint8_t *beg_of_payload, *end_of_payload;
    uint16_t biBitCount;
    uint32_t biClrUsed;

    if(sp == NULL)
        return RULE_NOMATCH;

    if(sp->payload == NULL)
        return RULE_NOMATCH;

    // flow:established, to_client;
    if (checkFlow(p, rule16415options[0]->option_u.flowFlags) <= 0 )
        return RULE_NOMATCH;

    //file_data;
#ifndef MISSINGFILEDATA
    if (fileData(p, rule16415options[1]->option_u.cursor, &cursor_normal) <= 0 )
        return RULE_NOMATCH;
#else
    if (setCursor(p, rule16415options[1]->option_u.cursor, &cursor_normal) <= 0 )
        return RULE_NOMATCH;
#endif

    // content:"RIFF", depth 0, fast_pattern;
    if (contentMatch(p, rule16415options[2]->option_u.content, &cursor_normal) <= 0)
        return RULE_NOMATCH;

    // content:"AVI ", offset 4, depth 4, relative;
    if (contentMatch(p, rule16415options[3]->option_u.content, &cursor_normal) <= 0)
        return RULE_NOMATCH;
    // content:"strl", offset 0, relative;
    if (contentMatch(p, rule16415options[4]->option_u.content, &cursor_normal) <= 0)
        return RULE_NOMATCH;
    // content:"vids", offset 8, depth 4, relative
    if (contentMatch(p, rule16415options[5]->option_u.content, &cursor_normal) <= 0)
        return RULE_NOMATCH;
    // content:"strf", depth 0, relative;
    if (contentMatch(p, rule16415options[6]->option_u.content, &cursor_normal) <= 0)
        return RULE_NOMATCH;

    if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0)
        return RULE_NOMATCH;

    if(cursor_normal + 40 > end_of_payload)
        return RULE_NOMATCH;

    //Jump past strf size DWORD and beginning of bitmapheader
    cursor_normal += 18;

    //extract biBitCount
    biBitCount = *cursor_normal++;
    biBitCount |= *cursor_normal++ << 8;

    //Jump to and extract biClrUsed
    cursor_normal += 16;
    biClrUsed = *cursor_normal++;
    biClrUsed |= *cursor_normal++ << 8;
    biClrUsed |= *cursor_normal++ << 16;
    biClrUsed |= *cursor_normal << 24;

    DEBUG_SO(printf("biBitCount = 0x%02x, biClrUsed = 0x%04x\n", biBitCount, biClrUsed));

    /* Avoid potential wrap and false positive.
     * A shift larger than uint32 will always be greater. */
    if (biBitCount >= 32)
        return RULE_NOMATCH;

    //Alert when...
    if (biClrUsed > (1 << biBitCount))
        return RULE_MATCH;

    return RULE_NOMATCH;
}