document the default privilege drop (Closes #34)
[prads.git] / src / dhcp.c
blobb2940202ba8b17e71e777a638d15657e4adcb771
1 #include "prads.h"
2 #include "config.h"
3 #include "sys_func.h"
4 #include "dhcp.h"
6 extern globalconfig config;
8 static int parse_dhcp_sig_opts(dhcp_fp_entry *sig, char* p);
9 static int parse_dhcp_sig_optreq(dhcp_fp_entry *sig, char* p);
10 static dhcp_fp_entry *alloc_dhcp_sig(dhcp_fp_entry *e);
11 static void free_dhcp_sigs(dhcp_fp_entry *e);
13 static const unsigned char vendcookie[] = { 99, 130, 83, 99 };
14 #define BOOTP_COOKIE_SIZE 4
15 #define PKT_MAXPAY 16
17 void print_dhcp_header(dhcp_header *dhcph)
19 plog("OP:%d\n",dhcph->op);
20 plog("HTYPE:%d\n",dhcph->htype);
21 plog("HLEN:%d\n",dhcph->hlen);
22 plog("HOPS:%d\n",dhcph->hops);
23 plog("XID:%d\n",dhcph->xid);
24 plog("SECS:%d\n",dhcph->secs);
25 plog("FLAGS:%d\n",dhcph->flags);
27 char dest[INET_ADDRSTRLEN];
28 inet_ntop(AF_INET,&(dhcph->ciaddr),dest,INET_ADDRSTRLEN + 1);
29 plog("CIP:%s\n",dest);
30 inet_ntop(AF_INET,&(dhcph->yiaddr),dest,INET_ADDRSTRLEN + 1);
31 plog("YIP:%s\n",dest);
32 inet_ntop(AF_INET,&(dhcph->siaddr),dest,INET_ADDRSTRLEN + 1);
33 plog("SIP:%s\n",dest);
34 inet_ntop(AF_INET,&(dhcph->giaddr),dest,INET_ADDRSTRLEN + 1);
35 plog("GIP:%s\n",dest);
36 plog("CHADDR:");
37 uint8_t i;
38 for(i = 0; i < 6; i++){
39 printf("%02hhx", dhcph->chaddr[i]);
40 if (i != dhcph->hlen-1)
41 printf(":");
43 plog("\n");
44 plog("SNAME:%s\n",dhcph->sname);
45 plog("FILE:%s\n",dhcph->file);
49 dhcp_fp_entry *dhcp_fingerprint(packetinfo *pi)
51 plog("Got DHCP packet:\n");
52 config.pr_s.dhcp_os_assets++;
54 uint8_t dhcp_header_length;
55 uint8_t *dhcp_mc;
56 uint8_t *dhcp_options;
57 uint8_t optlen = 0;
58 uint8_t dhcp_opt_type = 0;
59 uint8_t end_opt_parsing = 0;
62 dhcp_header *dhcph;
63 dhcph = (dhcp_header *) (pi->payload);
64 print_dhcp_header(dhcph);
66 dhcp_header_length = sizeof(dhcp_header);
67 dhcp_mc = (uint8_t *) (pi->payload + dhcp_header_length);
69 /* TODO: check and bail if not there */
70 //plog("Magic Cookie: %d%d%d%d\n", *dhcp_mc, *(dhcp_mc+1), *(dhcp_mc+2), *(dhcp_mc+3)); // 99 130 83 99
72 dhcp_options = (uint8_t *) dhcp_mc + BOOTP_COOKIE_SIZE;
73 uint8_t *optptr = dhcp_options;
74 uint8_t max_len = (pi->plen - dhcp_header_length - BOOTP_COOKIE_SIZE);
76 dhcp_fp_entry dhcpfp = {0}; //guarantee it's empty this sig
77 dhcpfp.ttl = pi->ip4->ip_ttl;
80 uint8_t optcnt = 0;
82 while (optlen < max_len) {
83 uint8_t i;
85 uint8_t opt = *(optptr);
86 uint8_t optsize = *(optptr+1);
87 uint8_t *optdata = optptr+2;
89 dhcpfp.opt[optcnt] = opt;
91 switch(opt) {
92 case DHCP_OPTION_TYPE: /* 53 */
93 if (optsize == 1) {
94 dhcp_opt_type = *optdata;
95 dhcpfp.type = dhcp_opt_type;
97 break;
98 case DHCP_OPTION_OPTIONREQ: /* 55 */
99 if (optsize > 0) {
100 uint8_t optreqcnt = 0;
101 for (i=2; i < optsize+2; i++) {
102 dhcpfp.optreq[optreqcnt] = *(optptr+i);
103 optreqcnt++;
105 dhcpfp.optreqcnt = optreqcnt;
107 break;
108 case DHCP_OPTION_CLASS_IDENTIFIER: /* 60 */
109 if (optsize > 0) {
110 dhcpfp.vc = calloc(1, optsize + 1);
111 strncpy(dhcpfp.vc, (char*) optdata, optsize);
113 break;
114 case DHCP_OPTION_PAD: /* 0 */
115 break;
116 case DHCP_OPTION_END: /* 255 */
117 end_opt_parsing = 1;
118 optcnt++;
119 break;
120 default:
121 break;
124 optptr = optptr + optsize + 2;
126 optlen = optlen + optsize + 2;
128 if (end_opt_parsing == 1) break;
130 /* Just to be sure */
131 if (*(optptr) != DHCP_OPTION_END) {
132 if (optptr + *(optptr+1) + 2 > pi->payload + pi->plen) break;
134 optcnt++;
136 dhcpfp.optcnt = optcnt;
138 print_dhcp_sig(&dhcpfp);
139 plog("\n");
141 dhcp_fp_entry *match = find_dhcp_match(&dhcpfp, pi);
143 #define OS_DHCP = 0x01
144 //update_asset_os(pi, OS_DHCP, NULL, &dhcpfp, tstamp);
145 return match;
148 dhcp_fp_entry *find_dhcp_match(dhcp_fp_entry *dhcpfp, packetinfo *pi)
150 dhcp_fp_entry* p;
151 uint32_t j;
153 //uint32_t hashsize; // = config.sig_hashsize;
155 //if(!hashsize)
156 // hashsize = DHCP_SIG_HASHSIZE;
158 uint32_t index;
160 index = (DHCP_SIGHASH(dhcpfp->type, dhcpfp->optcnt) % 331);
162 p = config.sig_dhcp[index];
163 while (p) {
164 /* Cheap and specific checks first... */
165 if (dhcpfp->type ^ p->type) { p = p->next; continue; }
166 if (dhcpfp->optcnt ^ p->optcnt) { p = p->next; continue; }
167 if (dhcpfp->optreqcnt ^ p->optreqcnt) { p = p->next; continue; }
169 /* Numbers agree. Let's check options 53 first */
170 if (dhcpfp->optreqcnt != 0) {
171 for (j=0;j<dhcpfp->optreqcnt;j++){
172 if (p->optreq[j] ^ dhcpfp->optreq[j]) goto continue_search;
176 /* Let's check options */
177 if (dhcpfp->optcnt != 0) {
178 for (j=0;j<dhcpfp->optcnt;j++){
179 if (p->opt[j] ^ dhcpfp->opt[j]) goto continue_search;
183 /* Numbers agree. Lets match Vendor Code */
184 if (p->vc != NULL && dhcpfp->vc != NULL) {
185 if (strcmp(p->vc, dhcpfp->vc) == 0) {
186 /* Huston - we have a match */
187 plog("[*] We have a match (");
188 print_dhcp_sig(p);
189 plog(")\n");
190 //plog("OS: %s\n",p->os);
191 //plog("DESC: %s\n",p->desc);
192 break;
194 } else {
195 /* Huston - we have a match */
196 plog("[*] We have a match (");
197 print_dhcp_sig(p);
198 plog(")\n");
199 //plog("OS: %s, ",p->os);
200 //plog("DESC: %s)\n",p->desc);
201 break;
204 continue_search:
205 p = p->next;
207 return dhcpfp;
210 void print_data(const uint8_t* data, uint16_t dlen) {
211 uint8_t tbuf[PKT_MAXPAY+2];
212 uint8_t* t = tbuf;
213 uint8_t i;
214 uint8_t max = dlen > PKT_MAXPAY ? PKT_MAXPAY : dlen;
216 if (!dlen) return;
218 for (i=0;i<max;i++) {
219 if (isprint(*data)) *(t++) = *data;
220 else if (!*data) *(t++) = '?';
221 else *(t++) = '.';
222 data++;
225 *t = 0;
227 plog("%s",tbuf);
230 int load_dhcp_sigs(const char *file, dhcp_fp_entry **dhcpsp[], int hashsize)
232 // sigp == dhcpsp
233 dhcp_fp_entry **sig; // output
234 uint32_t ln = 0;
235 FILE *f = fopen(file, "r");
236 char buf[MAXLINE];
237 char *p;
238 if (!f) {
239 perror("failed to open file");
240 return errno;
242 if(!dhcpsp){
243 perror("need a pointer to fill");
244 return -1;
246 if(!hashsize)
247 hashsize = DHCP_SIG_HASHSIZE;
248 if(*dhcpsp == NULL){
249 *dhcpsp = calloc(hashsize, sizeof(dhcp_fp_entry*));
250 sig = *dhcpsp;
253 while ((p = fgets(buf, sizeof(buf), f))) {
254 uint32_t l;
256 char opts[MAXLINE], optreq[MAXLINE], genre[MAXLINE], desc[MAXLINE];
257 char vc[MAXLINE];
258 char *gptr = genre;
259 uint32_t t, type;
260 dhcp_fp_entry asig = {0}; //guarantee it's empty this sig
261 dhcp_fp_entry *e;
263 ln++;
265 /* Remove leading and trailing blanks */
266 while (isspace(*p))
267 p++;
268 l = strlen(p);
269 while (l && isspace(*(p + l - 1)))
270 *(p + (l--) - 1) = 0;
272 /* Skip empty lines and comments */
273 if (!l)
274 continue;
275 if (*p == '#')
276 continue;
278 /* T4:64:1:60:M*,S,T,N,W7:.:Linux:2.6 (newer, 7) */
279 /* 53-OPTION:TTL:ALL-OPTIONS:55-OPTIONS:60-OPTIONS:OS:OS Details */
280 /* 1:128:53,116,61,12,60,55,43:1,15,3,6,44,46,47,31,33,121,249,43:MSFT 5.0:Windows:Windows XP SP3 */
281 if (sscanf
282 (p, "%d:%d:%[^:]:%[^:]:%[^:]:%[^:]:%[^:]",
283 &type, &t, opts, optreq, vc, genre, desc) != 7)
284 fatal("Syntax error in config line %d.\n", ln);
286 gptr = genre;
288 asig.type = type;
289 asig.ttl = t;
290 asig.os = strdup(gptr);
291 asig.desc = strdup(desc);
292 asig.vc = strdup(vc);
293 asig.line = ln;
295 parse_dhcp_sig_opts(&asig, opts);
296 parse_dhcp_sig_optreq(&asig, optreq);
298 uint32_t index = (DHCP_SIGHASH(asig.type, asig.optcnt) % 331);
299 e = sig[index];
301 if (!e) {
302 sig[index] = alloc_dhcp_sig(&asig);
303 } else {
304 int cc = 0;
305 // collision!
306 while (e->next){
307 e = e->next;
308 cc++;
311 fprintf(stderr, "hash collision: %s\n", p);
313 e->next = alloc_dhcp_sig(&asig);
317 fclose(f);
318 #ifdef DUMP_SIG_HASH
320 int i;
321 for (i = 0; i < sigcnt; i++) {
322 print_dhcp_sig(&sig[i]);
325 #endif
326 #ifdef DEBUG_HASH
328 int i;
329 dhcp_fp_entry *p;
330 printf("DHCP hash table layout: ");
331 for (i = 0; i < hashsize; i++) {
332 int z = 0;
333 p = sig[i];
334 while (p) {
335 p = p->next;
336 z++;
338 printf("%d ", z);
340 putchar('\n');
342 #endif /* DEBUG_HASH */
344 // if (check_collide)
345 // debug("[+] DHCP signature collision check successful.\n");
347 return 0;
350 /* run through the hash, free entries, then free hash */
351 void unload_dhcp_sigs(dhcp_fp_entry **sigp, int size)
353 int i = size;
354 dhcp_fp_entry *e;
355 while(i--){
356 e = sigp[i];
357 if (e)
358 free_dhcp_sigs(e);
359 sigp[i] = NULL; // clear
361 free(*sigp);
362 *sigp = NULL;
365 /* alloc_sig return a newly allocated copy of *e */
366 static dhcp_fp_entry *alloc_dhcp_sig(dhcp_fp_entry *e)
368 dhcp_fp_entry *n = calloc(1, sizeof(dhcp_fp_entry));
369 *n = *e; // copy
370 return n;
373 /* recursively free signatures */
374 static void free_dhcp_sigs(dhcp_fp_entry *e){
375 if(e->next)
376 free_dhcp_sigs(e->next);
377 free(e);
380 void dump_dhcp_sigs(dhcp_fp_entry *mysig[], int max)
382 int i;
383 for (i = 0; i < max; i++){
384 if (!mysig[i] || !mysig[i]->os)
385 continue;
386 print_dhcp_sig(mysig[i]);
390 void print_dhcp_sig(dhcp_fp_entry * e)
392 int32_t j;
394 plog("[%d:%d:",e->type,e->ttl);
395 for (j=0;j<e->optcnt;j++){
396 plog("%d",e->opt[j]);
397 if ((j+1) < (e->optcnt)) plog(",");
399 plog(":");
400 for (j=0;j<e->optreqcnt;j++){
401 plog("%d",e->optreq[j]);
402 if ((j+1) < (e->optreqcnt)) plog(",");
404 if (e->optreqcnt==0) plog(".");
405 plog(":");
406 if (e->vc == NULL) {
407 plog(".");
408 } else {
409 plog("%s",e->vc);
411 plog(":");
412 if (e->os == NULL) {
413 plog("unknown");
414 } else {
415 plog("%s",e->os);
417 plog(":");
418 if (e->desc == NULL) {
419 plog("unknown");
420 } else {
421 plog("%s",e->desc);
423 plog("]");
425 void print_dhcp_sigs(dhcp_fp_entry * e)
427 print_dhcp_sig(e);
428 if (e->next)
429 print_dhcp_sigs(e->next);
433 /* parse the option field of the signature line */
434 static int parse_dhcp_sig_opts(dhcp_fp_entry *sig, char* p)
436 uint8_t optcnt = 0;
437 if (*p == '.')
438 p++;
440 while (*p) {
442 if (!isdigit(*(p))) {
443 fatal("Bogus DHCP value in line %d.\n", sig->line);
444 } else {
445 if (!isdigit(*(p+1))) {
446 sig->opt[optcnt] = atoi(p);
447 } else if (!isdigit(*(p+2))) {
448 sig->opt[optcnt] = atoi(p);
449 p++;
450 } else if (!isdigit(*(p+3))) {
451 sig->opt[optcnt] = atoi(p);
452 p++;
453 p++;
457 if (++optcnt >= MAXDHCPOPTS)
458 fatal
459 ("Too many DHCP options specified in config line %d.\n",
460 sig->line);
462 * Skip separators
464 do {
465 p++;
466 } while (*p && !isdigit(*p));
468 sig->optcnt = optcnt;
469 return 0;
472 /* parse the option field of the signature line */
473 static int parse_dhcp_sig_optreq(dhcp_fp_entry *sig, char* p)
475 uint8_t optreqcnt = 0;
477 if (*p == '.')
478 p++;
480 while (*p) {
481 if (!isdigit(*(p))) {
482 fatal("Bogus DHCP value in line %d.\n", sig->line);
483 } else {
484 if (!isdigit(*(p + 1))) {
485 sig->optreq[optreqcnt] = atoi(p);
486 } else if (!isdigit(*(p + 2))) {
487 sig->optreq[optreqcnt] = atoi(p);
488 p++;
489 } else if (!isdigit(*(p + 3))) {
490 sig->optreq[optreqcnt] = atoi(p);
491 p++;
492 p++;
496 if (++optreqcnt >= MAXDHCPOPTS)
497 fatal
498 ("Too many DHCP request options specified in config line %d.\n",
499 sig->line);
501 * Skip separators
503 do {
504 p++;
505 } while (*p && !isdigit(*p));
507 sig->optreqcnt = optreqcnt;
508 return 0;