• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * dhcpcd - DHCP client daemon
3  * Copyright (c) 2006-2015 Roy Marples <roy@marples.name>
4  * All rights reserved
5 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/file.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <inttypes.h>
32 #include <stddef.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <time.h>
36 #include <unistd.h>
37 
38 #include "config.h"
39 #include "auth.h"
40 #include "crypt/crypt.h"
41 #include "dhcp.h"
42 #include "dhcp6.h"
43 #include "dhcpcd.h"
44 
45 #ifdef __sun
46 #define htonll
47 #define ntohll
48 #endif
49 
50 #ifndef htonll
51 #if (BYTE_ORDER == LITTLE_ENDIAN)
52 static inline uint64_t
htonll(uint64_t x)53 htonll(uint64_t x)
54 {
55 
56 	return (uint64_t)htonl((uint32_t)(x >> 32)) |
57 	    (uint64_t)htonl((uint32_t)(x & 0xffffffff)) << 32;
58 }
59 #else	/* (BYTE_ORDER == LITTLE_ENDIAN) */
60 #define htonll(x) (x)
61 #endif
62 #endif  /* htonll */
63 
64 #ifndef ntohll
65 #if (BYTE_ORDER == LITTLE_ENDIAN)
66 static inline uint64_t
ntohll(uint64_t x)67 ntohll(uint64_t x)
68 {
69 
70 	return (uint64_t)ntohl((uint32_t)(x >> 32)) |
71 	    (uint64_t)ntohl((uint32_t)(x & 0xffffffff)) << 32;
72 }
73 #else	/* (BYTE_ORDER == LITTLE_ENDIAN) */
74 #define ntohll(x) (x)
75 #endif
76 #endif  /* ntohll */
77 
78 #define HMAC_LENGTH	16
79 
80 void
dhcp_auth_reset(struct authstate * state)81 dhcp_auth_reset(struct authstate *state)
82 {
83 
84 	state->replay = 0;
85 	if (state->token) {
86 		free(state->token->key);
87 		free(state->token->realm);
88 		free(state->token);
89 		state->token = NULL;
90 	}
91 	if (state->reconf) {
92 		free(state->reconf->key);
93 		free(state->reconf->realm);
94 		free(state->reconf);
95 		state->reconf = NULL;
96 	}
97 }
98 
99 /*
100  * Authenticate a DHCP message.
101  * m and mlen refer to the whole message.
102  * t is the DHCP type, pass it 4 or 6.
103  * data and dlen refer to the authentication option within the message.
104  */
105 const struct token *
dhcp_auth_validate(struct authstate * state,const struct auth * auth,const uint8_t * m,size_t mlen,int mp,int mt,const uint8_t * data,size_t dlen)106 dhcp_auth_validate(struct authstate *state, const struct auth *auth,
107     const uint8_t *m, size_t mlen, int mp,  int mt,
108     const uint8_t *data, size_t dlen)
109 {
110 	uint8_t protocol, algorithm, rdm, *mm, type;
111 	uint64_t replay;
112 	uint32_t secretid;
113 	const uint8_t *d, *realm;
114 	size_t realm_len;
115 	const struct token *t;
116 	time_t now;
117 	uint8_t hmac[HMAC_LENGTH];
118 
119 	if (dlen < 3 + sizeof(replay)) {
120 		errno = EINVAL;
121 		return NULL;
122 	}
123 
124 	/* Ensure that d is inside m which *may* not be the case for DHPCPv4 */
125 	if (data < m || data > m + mlen || data + dlen > m + mlen) {
126 		errno = ERANGE;
127 		return NULL;
128 	}
129 
130 	d = data;
131 	protocol = *d++;
132 	algorithm = *d++;
133 	rdm = *d++;
134 	if (!(auth->options & DHCPCD_AUTH_SEND)) {
135 		/* If we didn't send any authorisation, it can only be a
136 		 * reconfigure key */
137 		if (protocol != AUTH_PROTO_RECONFKEY) {
138 			errno = EINVAL;
139 			return NULL;
140 		}
141 	} else if (protocol != auth->protocol ||
142 		    algorithm != auth->algorithm ||
143 		    rdm != auth->rdm)
144 	{
145 		/* As we don't require authentication, we should still
146 		 * accept a reconfigure key */
147 		if (protocol != AUTH_PROTO_RECONFKEY ||
148 		    auth->options & DHCPCD_AUTH_REQUIRE)
149 		{
150 			errno = EPERM;
151 			return NULL;
152 		}
153 	}
154 	dlen -= 3;
155 
156 	memcpy(&replay, d, sizeof(replay));
157 	replay = ntohll(replay);
158 	if (state->token) {
159 		if (state->replay == (replay ^ 0x8000000000000000ULL)) {
160 			/* We don't know if the singular point is increasing
161 			 * or decreasing. */
162 			errno = EPERM;
163 			return NULL;
164 		}
165 		if ((uint64_t)(replay - state->replay) <= 0) {
166 			/* Replay attack detected */
167 			errno = EPERM;
168 			return NULL;
169 		}
170 	}
171 	d+= sizeof(replay);
172 	dlen -= sizeof(replay);
173 
174 	realm = NULL;
175 	realm_len = 0;
176 
177 	/* Extract realm and secret.
178 	 * Rest of data is MAC. */
179 	switch (protocol) {
180 	case AUTH_PROTO_TOKEN:
181 		secretid = 0;
182 		break;
183 	case AUTH_PROTO_DELAYED:
184 		if (dlen < sizeof(secretid) + sizeof(hmac)) {
185 			errno = EINVAL;
186 			return NULL;
187 		}
188 		memcpy(&secretid, d, sizeof(secretid));
189 		d += sizeof(secretid);
190 		dlen -= sizeof(secretid);
191 		break;
192 	case AUTH_PROTO_DELAYEDREALM:
193 		if (dlen < sizeof(secretid) + sizeof(hmac)) {
194 			errno = EINVAL;
195 			return NULL;
196 		}
197 		realm_len = dlen - (sizeof(secretid) + sizeof(hmac));
198 		if (realm_len) {
199 			realm = d;
200 			d += realm_len;
201 			dlen -= realm_len;
202 		}
203 		memcpy(&secretid, d, sizeof(secretid));
204 		d += sizeof(secretid);
205 		dlen -= sizeof(secretid);
206 		break;
207 	case AUTH_PROTO_RECONFKEY:
208 		if (dlen != 1 + 16) {
209 			errno = EINVAL;
210 			return NULL;
211 		}
212 		type = *d++;
213 		dlen--;
214 		switch (type) {
215 		case 1:
216 			if ((mp == 4 && mt == DHCP_ACK) ||
217 			    (mp == 6 && mt == DHCP6_REPLY))
218 			{
219 				if (state->reconf == NULL) {
220 					state->reconf =
221 					    malloc(sizeof(*state->reconf));
222 					if (state->reconf == NULL)
223 						return NULL;
224 					state->reconf->key = malloc(16);
225 					if (state->reconf->key == NULL) {
226 						free(state->reconf);
227 						state->reconf = NULL;
228 						return NULL;
229 					}
230 					state->reconf->secretid = 0;
231 					state->reconf->expire = 0;
232 					state->reconf->realm = NULL;
233 					state->reconf->realm_len = 0;
234 					state->reconf->key_len = 16;
235 				}
236 				memcpy(state->reconf->key, d, 16);
237 			} else {
238 				errno = EINVAL;
239 				return NULL;
240 			}
241 			if (state->reconf == NULL)
242 				errno = ENOENT;
243 			/* Free the old token so we log acceptance */
244 			if (state->token) {
245 				free(state->token);
246 				state->token = NULL;
247 			}
248 			/* Nothing to validate, just accepting the key */
249 			return state->reconf;
250 		case 2:
251 			if (!((mp == 4 && mt == DHCP_FORCERENEW) ||
252 			    (mp == 6 && mt == DHCP6_RECONFIGURE)))
253 			{
254 				errno = EINVAL;
255 				return NULL;
256 			}
257 			if (state->reconf == NULL) {
258 				errno = ENOENT;
259 				return NULL;
260 			}
261 			t = state->reconf;
262 			goto gottoken;
263 		default:
264 			errno = EINVAL;
265 			return NULL;
266 		}
267 	default:
268 		errno = ENOTSUP;
269 		return NULL;
270 	}
271 
272 	/* Find a token for the realm and secret */
273 	secretid = ntohl(secretid);
274 	TAILQ_FOREACH(t, &auth->tokens, next) {
275 		if (t->secretid == secretid &&
276 		    t->realm_len == realm_len &&
277 		    (t->realm_len == 0 ||
278 		    memcmp(t->realm, realm, t->realm_len) == 0))
279 			break;
280 	}
281 	if (t == NULL) {
282 		errno = ESRCH;
283 		return NULL;
284 	}
285 	if (t->expire) {
286 		if (time(&now) == -1)
287 			return NULL;
288 		if (t->expire < now) {
289 			errno = EFAULT;
290 			return NULL;
291 		}
292 	}
293 
294 gottoken:
295 	/* First message from the server */
296 	if (state->token &&
297 	    (state->token->secretid != t->secretid ||
298 	    state->token->realm_len != t->realm_len ||
299 	    memcmp(state->token->realm, t->realm, t->realm_len)))
300 	{
301 		errno = EPERM;
302 		return NULL;
303 	}
304 
305 	/* Special case as no hashing needs to be done. */
306 	if (protocol == AUTH_PROTO_TOKEN) {
307 		if (dlen != t->key_len || memcmp(d, t->key, dlen)) {
308 			errno = EPERM;
309 			return NULL;
310 		}
311 		goto finish;
312 	}
313 
314 	/* Make a duplicate of the message, but zero out the MAC part */
315 	mm = malloc(mlen);
316 	if (mm == NULL)
317 		return NULL;
318 	memcpy(mm, m, mlen);
319 	memset(mm + (d - m), 0, dlen);
320 
321 	/* RFC3318, section 5.2 - zero giaddr and hops */
322 	if (mp == 4) {
323 		*(mm + offsetof(struct dhcp_message, hwopcount)) = '\0';
324 		memset(mm + offsetof(struct dhcp_message, giaddr), 0, 4);
325 	}
326 
327 	memset(hmac, 0, sizeof(hmac));
328 	switch (algorithm) {
329 	case AUTH_ALG_HMAC_MD5:
330 		hmac_md5(mm, mlen, t->key, t->key_len, hmac);
331 		break;
332 	default:
333 		errno = ENOSYS;
334 		free(mm);
335 		return NULL;
336 	}
337 
338 	free(mm);
339 	if (memcmp(d, &hmac, dlen)) {
340 		errno = EPERM;
341 		return NULL;
342 	}
343 
344 finish:
345 	/* If we got here then authentication passed */
346 	state->replay = replay;
347 	if (state->token == NULL) {
348 		/* We cannot just save a pointer because a reconfigure will
349 		 * recreate the token list. So we duplicate it. */
350 		state->token = malloc(sizeof(*state->token));
351 		if (state->token) {
352 			state->token->secretid = t->secretid;
353 			state->token->key = malloc(t->key_len);
354 			if (state->token->key) {
355 				state->token->key_len = t->key_len;
356 				memcpy(state->token->key, t->key, t->key_len);
357 			} else {
358 				free(state->token);
359 				state->token = NULL;
360 				return NULL;
361 			}
362 			if (t->realm_len) {
363 				state->token->realm = malloc(t->realm_len);
364 				if (state->token->realm) {
365 					state->token->realm_len = t->realm_len;
366 					memcpy(state->token->realm, t->realm,
367 					    t->realm_len);
368 				} else {
369 					free(state->token->key);
370 					free(state->token);
371 					state->token = NULL;
372 					return NULL;
373 				}
374 			} else {
375 				state->token->realm = NULL;
376 				state->token->realm_len = 0;
377 			}
378 		}
379 		/* If we cannot save the token, we must invalidate */
380 		if (state->token == NULL)
381 			return NULL;
382 	}
383 
384 	return t;
385 }
386 
387 static uint64_t
get_next_rdm_monotonic_counter(struct auth * auth)388 get_next_rdm_monotonic_counter(struct auth *auth)
389 {
390 	FILE *fp;
391 	uint64_t rdm;
392 #ifdef LOCK_EX
393 	int flocked;
394 #endif
395 
396 	fp = fopen(RDM_MONOFILE, "r+");
397 	if (fp == NULL) {
398 		if (errno != ENOENT)
399 			return ++auth->last_replay; /* report error? */
400 		fp = fopen(RDM_MONOFILE, "w");
401 		if (fp == NULL)
402 			return ++auth->last_replay; /* report error? */
403 #ifdef LOCK_EX
404 		flocked = flock(fileno(fp), LOCK_EX);
405 #endif
406 		rdm = 0;
407 	} else {
408 #ifdef LOCK_EX
409 		flocked = flock(fileno(fp), LOCK_EX);
410 #endif
411 		if (fscanf(fp, "0x%016" PRIu64, &rdm) != 1)
412 			rdm = 0; /* truncated? report error? */
413 	}
414 
415 	rdm++;
416 	if (fseek(fp, 0, SEEK_SET) == -1 ||
417 	    ftruncate(fileno(fp), 0) == -1 ||
418 	    fprintf(fp, "0x%016" PRIu64 "\n", rdm) != 19)
419 	{
420 		if (!auth->last_replay_set) {
421 			auth->last_replay = rdm;
422 			auth->last_replay_set = 1;
423 		} else
424 			rdm = ++auth->last_replay;
425 		/* report error? */
426 	}
427 	fflush(fp);
428 #ifdef LOCK_EX
429 	if (flocked == 0)
430 		flock(fileno(fp), LOCK_UN);
431 #endif
432 	fclose(fp);
433 	return rdm;
434 }
435 
436 #define JAN_1970       2208988800U    /* 1970 - 1900 in seconds */
437 static uint64_t
get_next_rdm_monotonic_clock(struct auth * auth)438 get_next_rdm_monotonic_clock(struct auth *auth)
439 {
440 	struct timespec ts;
441 	uint32_t pack[2];
442 	double frac;
443 	uint64_t rdm;
444 
445 	if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
446 		return ++auth->last_replay; /* report error? */
447 	pack[0] = htonl((uint32_t)ts.tv_sec + JAN_1970);
448 	frac = ((double)ts.tv_nsec / 1e9 * 0x100000000ULL);
449 	pack[1] = htonl((uint32_t)frac);
450 
451 	memcpy(&rdm, &pack, sizeof(rdm));
452 	return rdm;
453 }
454 
455 static uint64_t
get_next_rdm_monotonic(struct auth * auth)456 get_next_rdm_monotonic(struct auth *auth)
457 {
458 
459 	if (auth->options & DHCPCD_AUTH_RDM_COUNTER)
460 		return get_next_rdm_monotonic_counter(auth);
461 	return get_next_rdm_monotonic_clock(auth);
462 }
463 
464 /*
465  * Encode a DHCP message.
466  * Either we know which token to use from the server response
467  * or we are using a basic configuration token.
468  * token is the token to encrypt with.
469  * m and mlen refer to the whole message.
470  * mp is the DHCP type, pass it 4 or 6.
471  * mt is the DHCP message type.
472  * data and dlen refer to the authentication option within the message.
473  */
474 ssize_t
dhcp_auth_encode(struct auth * auth,const struct token * t,uint8_t * m,size_t mlen,int mp,int mt,uint8_t * data,size_t dlen)475 dhcp_auth_encode(struct auth *auth, const struct token *t,
476     uint8_t *m, size_t mlen, int mp, int mt,
477     uint8_t *data, size_t dlen)
478 {
479 	uint64_t rdm;
480 	uint8_t hmac[HMAC_LENGTH];
481 	time_t now;
482 	uint8_t hops, *p, info;
483 	uint32_t giaddr, secretid;
484 
485 	if (auth->protocol == 0 && t == NULL) {
486 		TAILQ_FOREACH(t, &auth->tokens, next) {
487 			if (t->secretid == 0 &&
488 			    t->realm_len == 0)
489 			break;
490 		}
491 		if (t == NULL) {
492 			errno = EINVAL;
493 			return -1;
494 		}
495 		if (t->expire) {
496 			if (time(&now) == -1)
497 				return -1;
498 			if (t->expire < now) {
499 				errno = EPERM;
500 				return -1;
501 			}
502 		}
503 	}
504 
505 	switch(auth->protocol) {
506 	case AUTH_PROTO_TOKEN:
507 	case AUTH_PROTO_DELAYED:
508 	case AUTH_PROTO_DELAYEDREALM:
509 		/* We don't ever send a reconf key */
510 		break;
511 	default:
512 		errno = ENOTSUP;
513 		return -1;
514 	}
515 
516 	switch(auth->algorithm) {
517 	case AUTH_ALG_HMAC_MD5:
518 		break;
519 	default:
520 		errno = ENOTSUP;
521 		return -1;
522 	}
523 
524 	switch(auth->rdm) {
525 	case AUTH_RDM_MONOTONIC:
526 		break;
527 	default:
528 		errno = ENOTSUP;
529 		return -1;
530 	}
531 
532 	/* DISCOVER or INFORM messages don't write auth info */
533 	if ((mp == 4 && (mt == DHCP_DISCOVER || mt == DHCP_INFORM)) ||
534 	    (mp == 6 && (mt == DHCP6_SOLICIT || mt == DHCP6_INFORMATION_REQ)))
535 		info = 0;
536 	else
537 		info = 1;
538 
539 	/* Work out the auth area size.
540 	 * We only need to do this for DISCOVER messages */
541 	if (data == NULL) {
542 		dlen = 1 + 1 + 1 + 8;
543 		switch(auth->protocol) {
544 		case AUTH_PROTO_TOKEN:
545 			dlen += t->key_len;
546 			break;
547 		case AUTH_PROTO_DELAYEDREALM:
548 			if (info && t)
549 				dlen += t->realm_len;
550 			/* FALLTHROUGH */
551 		case AUTH_PROTO_DELAYED:
552 			if (info && t)
553 				dlen += sizeof(t->secretid) + sizeof(hmac);
554 			break;
555 		}
556 		return (ssize_t)dlen;
557 	}
558 
559 	if (dlen < 1 + 1 + 1 + 8) {
560 		errno = ENOBUFS;
561 		return -1;
562 	}
563 
564 	/* Ensure that d is inside m which *may* not be the case for DHPCPv4 */
565 	if (data < m || data > m + mlen || data + dlen > m + mlen) {
566 		errno = ERANGE;
567 		return -1;
568 	}
569 
570 	/* Write out our option */
571 	*data++ = auth->protocol;
572 	*data++ = auth->algorithm;
573 	*data++ = auth->rdm;
574 	switch (auth->rdm) {
575 	case AUTH_RDM_MONOTONIC:
576 		rdm = get_next_rdm_monotonic(auth);
577 		break;
578 	default:
579 		/* This block appeases gcc, clang doesn't need it */
580 		rdm = get_next_rdm_monotonic(auth);
581 		break;
582 	}
583 	rdm = htonll(rdm);
584 	memcpy(data, &rdm, 8);
585 	data += 8;
586 	dlen -= 1 + 1 + 1 + 8;
587 
588 	/* Special case as no hashing needs to be done. */
589 	if (auth->protocol == AUTH_PROTO_TOKEN) {
590 		/* Should be impossible, but still */
591 		if (t == NULL) {
592 			errno = EINVAL;
593 			return -1;
594 		}
595 		if (dlen < t->key_len) {
596 			errno =	ENOBUFS;
597 			return -1;
598 		}
599 		memcpy(data, t->key, t->key_len);
600 		return (ssize_t)(dlen - t->key_len);
601 	}
602 
603 	/* DISCOVER or INFORM messages don't write auth info */
604 	if (!info)
605 		return (ssize_t)dlen;
606 
607 	/* Loading a saved lease without an authentication option */
608 	if (t == NULL)
609 		return 0;
610 
611 	/* Write out the Realm */
612 	if (auth->protocol == AUTH_PROTO_DELAYEDREALM) {
613 		if (dlen < t->realm_len) {
614 			errno = ENOBUFS;
615 			return -1;
616 		}
617 		memcpy(data, t->realm, t->realm_len);
618 		data += t->realm_len;
619 		dlen -= t->realm_len;
620 	}
621 
622 	/* Write out the SecretID */
623 	if (auth->protocol == AUTH_PROTO_DELAYED ||
624 	    auth->protocol == AUTH_PROTO_DELAYEDREALM)
625 	{
626 		if (dlen < sizeof(t->secretid)) {
627 			errno = ENOBUFS;
628 			return -1;
629 		}
630 		secretid = htonl(t->secretid);
631 		memcpy(data, &secretid, sizeof(secretid));
632 		data += sizeof(secretid);
633 		dlen -= sizeof(secretid);
634 	}
635 
636 	/* Zero what's left, the MAC */
637 	memset(data, 0, dlen);
638 
639 	/* RFC3318, section 5.2 - zero giaddr and hops */
640 	if (mp == 4) {
641 		p = m + offsetof(struct dhcp_message, hwopcount);
642 		hops = *p;
643 		*p = '\0';
644 		p = m + offsetof(struct dhcp_message, giaddr);
645 		memcpy(&giaddr, p, sizeof(giaddr));
646 		memset(p, 0, sizeof(giaddr));
647 	} else {
648 		/* appease GCC again */
649 		hops = 0;
650 		giaddr = 0;
651 	}
652 
653 	/* Create our hash and write it out */
654 	switch(auth->algorithm) {
655 	case AUTH_ALG_HMAC_MD5:
656 		hmac_md5(m, mlen, t->key, t->key_len, hmac);
657 		memcpy(data, hmac, sizeof(hmac));
658 		break;
659 	}
660 
661 	/* RFC3318, section 5.2 - restore giaddr and hops */
662 	if (mp == 4) {
663 		p = m + offsetof(struct dhcp_message, hwopcount);
664 		*p = hops;
665 		p = m + offsetof(struct dhcp_message, giaddr);
666 		memcpy(p, &giaddr, sizeof(giaddr));
667 	}
668 
669 	/* Done! */
670 	return (int)(dlen - sizeof(hmac)); /* should be zero */
671 }
672