/*************************************************************************** struct { ProtocolVersion server_version; Random random; SessionID session_id; CipherSuite cipher_suite; CompressionMethod compression_method; } ServerHello; ***************************************************************************/ static void server_hello( const struct Banner1 *banner1, void *banner1_private, struct ProtocolState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout) { struct SSL_SERVER_HELLO *hello = &pstate->sub.ssl.x.server_hello; unsigned state = hello->state; unsigned remaining = hello->remaining; unsigned i; enum { VERSION_MAJOR, VERSION_MINOR, TIME0, TIME1, TIME2, TIME3, RANDOM, SESSION_LENGTH, SESSION_ID, CIPHER0, CIPHER1, COMPRESSION, LENGTH0, LENGTH1, UNKNOWN, }; UNUSEDPARM(banout); UNUSEDPARM(banner1_private); UNUSEDPARM(banner1); for (i=0; i<length; i++) switch (state) { case VERSION_MAJOR: hello->version_major = px[i]; DROPDOWN(i,length,state); case VERSION_MINOR: hello->version_minor = px[i]; if (hello->version_major > 3 || hello->version_minor > 4) { state = UNKNOWN; break; } hello->timestamp = 0; DROPDOWN(i,length,state); case TIME0: hello->timestamp <<= 8; hello->timestamp |= px[i]; DROPDOWN(i,length,state); case TIME1: hello->timestamp <<= 8; hello->timestamp |= px[i]; DROPDOWN(i,length,state); case TIME2: hello->timestamp <<= 8; hello->timestamp |= px[i]; DROPDOWN(i,length,state); case TIME3: hello->timestamp <<= 8; hello->timestamp |= px[i]; remaining = 28; DROPDOWN(i,length,state); case RANDOM: { unsigned len = (unsigned)length-i; if (len > remaining) len = remaining; remaining -= len; i += len-1; if (remaining != 0) { break; } } DROPDOWN(i,length,state); case SESSION_LENGTH: remaining = px[i]; DROPDOWN(i,length,state); case SESSION_ID: { unsigned len = (unsigned)length-i; if (len > remaining) len = remaining; remaining -= len; i += len-1; if (remaining != 0) { break; } } hello->cipher_suite = 0; DROPDOWN(i,length,state); case CIPHER0: hello->cipher_suite <<= 8; hello->cipher_suite |= px[i]; DROPDOWN(i,length,state); case CIPHER1: hello->cipher_suite <<= 8; hello->cipher_suite |= px[i]; /* cipher-suite recorded here */ { char foo[64]; sprintf_s(foo, sizeof(foo), "cipher:0x%x", hello->cipher_suite); banout_append(banout, PROTO_SSL3, foo, strlen(foo)); } DROPDOWN(i,length,state); case COMPRESSION: hello->compression_method = px[i]; DROPDOWN(i,length,state); case LENGTH0: remaining = px[i]; DROPDOWN(i,length,state); case LENGTH1: remaining <<= 8; remaining |= px[i]; DROPDOWN(i,length,state); break; case UNKNOWN: default: i = (unsigned)length; } hello->state = state; hello->remaining = remaining; }
unsigned banner1_parse( const struct Banner1 *banner1, struct ProtocolState *tcb_state, const unsigned char *px, size_t length, struct BannerOutput *banout) { size_t x; unsigned offset = 0; switch (tcb_state->app_proto) { case PROTO_NONE: case PROTO_HEUR: x = smack_search_next( banner1->smack, &tcb_state->state, px, &offset, (unsigned)length); if (x != SMACK_NOT_FOUND && !(x == PROTO_SSL3 && !tcb_state->is_sent_sslhello)) { unsigned i; /* re-read the stuff that we missed */ for (i=0; patterns[i].id && patterns[i].id != tcb_state->app_proto; i++) ; /* Kludge: patterns look confusing, so add port info to the * pattern */ switch (x) { case PROTO_FTP2: if (tcb_state->port == 25 || tcb_state->port == 587) x = PROTO_SMTP; break; } tcb_state->app_proto = (unsigned short)x; /* reset the state back again */ tcb_state->state = 0; /* If there is any data from a previous packet, re-parse that */ { const unsigned char *s = banout_string(banout, PROTO_HEUR); unsigned s_len = banout_string_length(banout, PROTO_HEUR); if (s && s_len) banner1_parse( banner1, tcb_state, s, s_len, banout); } banner1_parse( banner1, tcb_state, px, length, banout); } else { banout_append(banout, PROTO_HEUR, px, length); } break; case PROTO_SSH1: case PROTO_SSH2: case PROTO_FTP1: case PROTO_FTP2: case PROTO_SMTP: case PROTO_POP3: case PROTO_IMAP4: /* generic text-based parser * TODO: in future, need to split these into separate protocols, * especially when binary parsing is added to SSH */ banner_ssh.parse( banner1, banner1->http_fields, tcb_state, px, length, banout); break; case PROTO_HTTP: banner_http.parse( banner1, banner1->http_fields, tcb_state, px, length, banout); break; case PROTO_SSL3: banner_ssl.parse( banner1, banner1->http_fields, tcb_state, px, length, banout); break; default: fprintf(stderr, "banner1: internal error\n"); break; } return tcb_state->app_proto; }