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)
52static inline uint64_t
53htonll(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)
66static inline uint64_t
67ntohll(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
80void
81dhcp_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 */
105const struct token *
106dhcp_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
294gottoken:
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
344finish:
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
387static uint64_t
388get_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 */
437static uint64_t
438get_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
455static uint64_t
456get_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 */
474ssize_t
475dhcp_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