blob: 4bfbfdc587b458c70eb9dbec58483d72a813b7c3 [file] [log] [blame]
Samuel Tand7ed8512015-08-13 16:11:35 -07001/*
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}