/* 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)
// #if 0 // Don't compile the detection functions if they're not used
int rule18101eval(void *p) {
   const uint8_t *cursor_normal = 0;
   SFSnortPacket *sp = (SFSnortPacket *) p;
   const uint8_t *end_of_payload;

   BER_ELEMENT ber_element;

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

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

   BER_DATA(0x30);
   BER_SKIP(0x02);

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

   // Exploit condition is SearchRequest (0x63) or ModifyDNRequest (0x6c) with
   // large size.  We're going with 1000 as a max size, apparently.
   if(ber_element.data_len > 1000 && (ber_element.type == 0x63 || ber_element.type == 0x6c))
      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 rule27906eval(void *p) {
   const u_int8_t *cursor_normal = 0, *end_of_payload;
   SFSnortPacket *sp = (SFSnortPacket *) p;
   BER_ELEMENT kerberos_string;
   int i;

   if(sp == NULL)
      return RULE_NOMATCH;

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

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

   BER_DATA(0x6c);          // KDC-REQ [12]
   BER_DATA(0x30);          // SEQUENCE [16]
   BER_SKIP(0xA1);          // pvno [1]
   BER_SKIP(0xA2);          // msg-type [2]

   // if optional PA-DATA exists, skip it
   // 10 1 00011 (context-specific, structured, tag number 3)
   if(cursor_normal+1 > end_of_payload)
      return RULE_NOMATCH;
   if(*cursor_normal == 0xA3)
      BER_SKIP(0xA3);

   // KDC-REQ-BODY [4] ::= SEQUENCE [16]
   //    kdc-options [0] 
   //    realm       [2]  server's realm
   //    sname       [3]  PrincipalName
   BER_DATA(0xA4);  // KDC-REQ-BODY
   BER_DATA(0x30);  // SEQUENCE
   BER_SKIP(0xA0);  // kdc-options
   BER_SKIP(0xA2);  // realm

   // PrincipalName [3] ::= SEQUENCE [16]
   //    name-type   [0] Int32
   //    name-string [1] SEQUENCE [16] of KerberosString
   BER_DATA(0xA3);
   BER_DATA(0x30);
   BER_SKIP(0xA0);
   BER_DATA(0xA1);
   BER_DATA(0x30);

   // check up to 20 strings for the vulnerable condition
   for(i=0; (ber_get_element(sp, cursor_normal, &kerberos_string) >= 0) && (i < 20); i++) {
      // verify we are looking at a string element
      if(kerberos_string.type != 0x1b)
         return RULE_NOMATCH;

      DEBUG_SO(fprintf(stderr,"kerberos_string:\n data_len = 0x%02x\n",kerberos_string.data_len);)

      // vulnerable condition is kerberos_string.data_len == 0
      if(kerberos_string.data_len == 0)
         return RULE_MATCH;

      // Move to the end of the current element.  Guaranteed to move us forward in the packet.
      cursor_normal += kerberos_string.total_len;
   }
/* detection functions */
int rule36153eval(void *p) {
   const uint8_t *cursor_normal = 0, *end_of_buffer,
                 *next_mod_item, *next_msg;
   SFSnortPacket *sp = (SFSnortPacket *) p;
   BER_ELEMENT msg, req, mod, attribute;
   int i,j, bytes_available, stop;
   unsigned int subtag_len;

   if(sp == NULL)
      return RULE_NOMATCH;

   if(sp->payload == NULL)
      return RULE_NOMATCH;
   
   // flow:established, to_server;
   if(checkFlow(p, rule36153options[0]->option_u.flowFlags) <= 0)
      return RULE_NOMATCH;
   
   if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &cursor_normal, &end_of_buffer) <= 0)
      return RULE_NOMATCH;

   // LDAPMessage ::= SEQUENCE [16]
   if(ber_get_element(sp, cursor_normal, &msg) < 0)
      return RULE_NOMATCH;

   if(msg.type != 0x30)
      return RULE_NOMATCH;

   // calculate position of next msg
   next_msg = cursor_normal + msg.total_len;

   // integer overflow check
   if(next_msg < cursor_normal)
      return RULE_NOMATCH;

   // move cursor to messageID
   cursor_normal = msg.data.data_ptr;

   // messageID [2]
   BER_SKIP(0x02);

   // Request ::= APPLICATION [6]
   if(ber_get_element(sp, cursor_normal, &req) < 0)
      return RULE_NOMATCH;

   // move cursor to LDAPDN
   cursor_normal = req.data.data_ptr; 

   if(req.type != 0x66)
   {
      // check second msg for modifyRequest
      cursor_normal = next_msg;
      BER_DATA(0x30); // LDAPMessage 
      BER_SKIP(0x02); // messageID
      BER_DATA(0x66); // Request
   }

   BER_SKIP(0x04);   // LDAPDN [4]
   BER_DATA(0x30);   // modification items ::= SEQUENCE [16]

   // check up to 5 modification items 
   for(i=0; i<5; i++, cursor_normal = next_mod_item)
   {
      // modification item ::= SEQUENCE [16]
      if(ber_get_element(sp, cursor_normal, &mod) < 0)
         return RULE_NOMATCH;

      if(mod.type != 0x30)
         return RULE_NOMATCH;

      DEBUG_SO(fprintf(stderr,"mod item [0x%04X]\n", mod.data_len);)

      // calculate position of next mod item
      next_mod_item = cursor_normal + mod.total_len;

      // integer overflow check
      if(next_mod_item < cursor_normal)
         return RULE_NOMATCH;

      // if modification item data_len < 256 skip
      if(mod.data_len < 256)
         continue;

      // move cursor to operation 
      cursor_normal = mod.data.data_ptr;

      BER_SKIP(0x0A); // operation [10]
      BER_DATA(0x30); // modification ::= SEQUENCE [16]

      // attribute [4]
      bytes_available = ber_get_element(sp, cursor_normal, &attribute);

      if(bytes_available <= 0)
         return RULE_NOMATCH;

      if(attribute.type != 0x04)
         return RULE_NOMATCH;

      DEBUG_SO(fprintf(stderr," attribute [0x%04X]\n", attribute.data_len);)

      // if the attribute len is < 256
      // it cannot possibly contain the vuln, skip
      if(attribute.data_len < 256)