2 __RCSID("$NetBSD: auth.c,v 1.10 2015/07/09 10:15:34 roy Exp $");
5 * dhcpcd - DHCP client daemon
6 * Copyright (c) 2006-2015 Roy Marples <roy@marples.name>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 #include "crypt/crypt.h"
54 #if (BYTE_ORDER == LITTLE_ENDIAN)
55 static inline uint64_t
59 return (uint64_t)htonl((uint32_t)(x
>> 32)) |
60 (uint64_t)htonl((uint32_t)(x
& 0xffffffff)) << 32;
62 #else /* (BYTE_ORDER == LITTLE_ENDIAN) */
68 #if (BYTE_ORDER == LITTLE_ENDIAN)
69 static inline uint64_t
73 return (uint64_t)ntohl((uint32_t)(x
>> 32)) |
74 (uint64_t)ntohl((uint32_t)(x
& 0xffffffff)) << 32;
76 #else /* (BYTE_ORDER == LITTLE_ENDIAN) */
81 #define HMAC_LENGTH 16
84 dhcp_auth_reset(struct authstate
*state
)
89 free(state
->token
->key
);
90 free(state
->token
->realm
);
95 free(state
->reconf
->key
);
96 free(state
->reconf
->realm
);
103 * Authenticate a DHCP message.
104 * m and mlen refer to the whole message.
105 * t is the DHCP type, pass it 4 or 6.
106 * data and dlen refer to the authentication option within the message.
109 dhcp_auth_validate(struct authstate
*state
, const struct auth
*auth
,
110 const uint8_t *m
, size_t mlen
, int mp
, int mt
,
111 const uint8_t *data
, size_t dlen
)
113 uint8_t protocol
, algorithm
, rdm
, *mm
, type
;
116 const uint8_t *d
, *realm
;
118 const struct token
*t
;
120 uint8_t hmac
[HMAC_LENGTH
];
122 if (dlen
< 3 + sizeof(replay
)) {
127 /* Ensure that d is inside m which *may* not be the case for DHPCPv4 */
128 if (data
< m
|| data
> m
+ mlen
|| data
+ dlen
> m
+ mlen
) {
137 if (!(auth
->options
& DHCPCD_AUTH_SEND
)) {
138 /* If we didn't send any authorisation, it can only be a
140 if (protocol
!= AUTH_PROTO_RECONFKEY
) {
144 } else if (protocol
!= auth
->protocol
||
145 algorithm
!= auth
->algorithm
||
148 /* As we don't require authentication, we should still
149 * accept a reconfigure key */
150 if (protocol
!= AUTH_PROTO_RECONFKEY
||
151 auth
->options
& DHCPCD_AUTH_REQUIRE
)
159 memcpy(&replay
, d
, sizeof(replay
));
160 replay
= ntohll(replay
);
162 if (state
->replay
== (replay
^ 0x8000000000000000ULL
)) {
163 /* We don't know if the singular point is increasing
168 if ((uint64_t)(replay
- state
->replay
) <= 0) {
169 /* Replay attack detected */
175 dlen
-= sizeof(replay
);
180 /* Extract realm and secret.
181 * Rest of data is MAC. */
183 case AUTH_PROTO_TOKEN
:
186 case AUTH_PROTO_DELAYED
:
187 if (dlen
< sizeof(secretid
) + sizeof(hmac
)) {
191 memcpy(&secretid
, d
, sizeof(secretid
));
192 d
+= sizeof(secretid
);
193 dlen
-= sizeof(secretid
);
195 case AUTH_PROTO_DELAYEDREALM
:
196 if (dlen
< sizeof(secretid
) + sizeof(hmac
)) {
200 realm_len
= dlen
- (sizeof(secretid
) + sizeof(hmac
));
206 memcpy(&secretid
, d
, sizeof(secretid
));
207 d
+= sizeof(secretid
);
208 dlen
-= sizeof(secretid
);
210 case AUTH_PROTO_RECONFKEY
:
211 if (dlen
!= 1 + 16) {
219 if ((mp
== 4 && mt
== DHCP_ACK
) ||
220 (mp
== 6 && mt
== DHCP6_REPLY
))
222 if (state
->reconf
== NULL
) {
224 malloc(sizeof(*state
->reconf
));
225 if (state
->reconf
== NULL
)
227 state
->reconf
->key
= malloc(16);
228 if (state
->reconf
->key
== NULL
) {
230 state
->reconf
= NULL
;
233 state
->reconf
->secretid
= 0;
234 state
->reconf
->expire
= 0;
235 state
->reconf
->realm
= NULL
;
236 state
->reconf
->realm_len
= 0;
237 state
->reconf
->key_len
= 16;
239 memcpy(state
->reconf
->key
, d
, 16);
244 if (state
->reconf
== NULL
)
246 /* Free the old token so we log acceptance */
251 /* Nothing to validate, just accepting the key */
252 return state
->reconf
;
254 if (!((mp
== 4 && mt
== DHCP_FORCERENEW
) ||
255 (mp
== 6 && mt
== DHCP6_RECONFIGURE
)))
260 if (state
->reconf
== NULL
) {
275 /* Find a token for the realm and secret */
276 secretid
= ntohl(secretid
);
277 TAILQ_FOREACH(t
, &auth
->tokens
, next
) {
278 if (t
->secretid
== secretid
&&
279 t
->realm_len
== realm_len
&&
280 (t
->realm_len
== 0 ||
281 memcmp(t
->realm
, realm
, t
->realm_len
) == 0))
289 if (time(&now
) == -1)
291 if (t
->expire
< now
) {
298 /* First message from the server */
300 (state
->token
->secretid
!= t
->secretid
||
301 state
->token
->realm_len
!= t
->realm_len
||
302 memcmp(state
->token
->realm
, t
->realm
, t
->realm_len
)))
308 /* Special case as no hashing needs to be done. */
309 if (protocol
== AUTH_PROTO_TOKEN
) {
310 if (dlen
!= t
->key_len
|| memcmp(d
, t
->key
, dlen
)) {
317 /* Make a duplicate of the message, but zero out the MAC part */
322 memset(mm
+ (d
- m
), 0, dlen
);
324 /* RFC3318, section 5.2 - zero giaddr and hops */
326 *(mm
+ offsetof(struct dhcp_message
, hwopcount
)) = '\0';
327 memset(mm
+ offsetof(struct dhcp_message
, giaddr
), 0, 4);
330 memset(hmac
, 0, sizeof(hmac
));
332 case AUTH_ALG_HMAC_MD5
:
333 hmac_md5(mm
, mlen
, t
->key
, t
->key_len
, hmac
);
342 if (memcmp(d
, &hmac
, dlen
)) {
348 /* If we got here then authentication passed */
349 state
->replay
= replay
;
350 if (state
->token
== NULL
) {
351 /* We cannot just save a pointer because a reconfigure will
352 * recreate the token list. So we duplicate it. */
353 state
->token
= malloc(sizeof(*state
->token
));
355 state
->token
->secretid
= t
->secretid
;
356 state
->token
->key
= malloc(t
->key_len
);
357 if (state
->token
->key
) {
358 state
->token
->key_len
= t
->key_len
;
359 memcpy(state
->token
->key
, t
->key
, t
->key_len
);
366 state
->token
->realm
= malloc(t
->realm_len
);
367 if (state
->token
->realm
) {
368 state
->token
->realm_len
= t
->realm_len
;
369 memcpy(state
->token
->realm
, t
->realm
,
372 free(state
->token
->key
);
378 state
->token
->realm
= NULL
;
379 state
->token
->realm_len
= 0;
382 /* If we cannot save the token, we must invalidate */
383 if (state
->token
== NULL
)
391 get_next_rdm_monotonic_counter(struct auth
*auth
)
399 fp
= fopen(RDM_MONOFILE
, "r+");
402 return ++auth
->last_replay
; /* report error? */
403 fp
= fopen(RDM_MONOFILE
, "w");
405 return ++auth
->last_replay
; /* report error? */
407 flocked
= flock(fileno(fp
), LOCK_EX
);
412 flocked
= flock(fileno(fp
), LOCK_EX
);
414 if (fscanf(fp
, "0x%016" PRIu64
, &rdm
) != 1)
415 rdm
= 0; /* truncated? report error? */
419 if (fseek(fp
, 0, SEEK_SET
) == -1 ||
420 ftruncate(fileno(fp
), 0) == -1 ||
421 fprintf(fp
, "0x%016" PRIu64
"\n", rdm
) != 19 ||
424 if (!auth
->last_replay_set
) {
425 auth
->last_replay
= rdm
;
426 auth
->last_replay_set
= 1;
428 rdm
= ++auth
->last_replay
;
433 flock(fileno(fp
), LOCK_UN
);
439 #define JAN_1970 2208988800U /* 1970 - 1900 in seconds */
441 get_next_rdm_monotonic_clock(struct auth
*auth
)
448 if (clock_gettime(CLOCK_REALTIME
, &ts
) != 0)
449 return ++auth
->last_replay
; /* report error? */
450 pack
[0] = htonl((uint32_t)ts
.tv_sec
+ JAN_1970
);
451 frac
= ((double)ts
.tv_nsec
/ 1e9
* 0x100000000ULL
);
452 pack
[1] = htonl((uint32_t)frac
);
454 memcpy(&rdm
, &pack
, sizeof(rdm
));
459 get_next_rdm_monotonic(struct auth
*auth
)
462 if (auth
->options
& DHCPCD_AUTH_RDM_COUNTER
)
463 return get_next_rdm_monotonic_counter(auth
);
464 return get_next_rdm_monotonic_clock(auth
);
468 * Encode a DHCP message.
469 * Either we know which token to use from the server response
470 * or we are using a basic configuration token.
471 * token is the token to encrypt with.
472 * m and mlen refer to the whole message.
473 * mp is the DHCP type, pass it 4 or 6.
474 * mt is the DHCP message type.
475 * data and dlen refer to the authentication option within the message.
478 dhcp_auth_encode(struct auth
*auth
, const struct token
*t
,
479 uint8_t *m
, size_t mlen
, int mp
, int mt
,
480 uint8_t *data
, size_t dlen
)
483 uint8_t hmac
[HMAC_LENGTH
];
485 uint8_t hops
, *p
, info
;
486 uint32_t giaddr
, secretid
;
488 if (auth
->protocol
== 0 && t
== NULL
) {
489 TAILQ_FOREACH(t
, &auth
->tokens
, next
) {
490 if (t
->secretid
== 0 &&
499 if (time(&now
) == -1)
501 if (t
->expire
< now
) {
508 switch(auth
->protocol
) {
509 case AUTH_PROTO_TOKEN
:
510 case AUTH_PROTO_DELAYED
:
511 case AUTH_PROTO_DELAYEDREALM
:
512 /* We don't ever send a reconf key */
519 switch(auth
->algorithm
) {
520 case AUTH_ALG_HMAC_MD5
:
528 case AUTH_RDM_MONOTONIC
:
535 /* DISCOVER or INFORM messages don't write auth info */
536 if ((mp
== 4 && (mt
== DHCP_DISCOVER
|| mt
== DHCP_INFORM
)) ||
537 (mp
== 6 && (mt
== DHCP6_SOLICIT
|| mt
== DHCP6_INFORMATION_REQ
)))
542 /* Work out the auth area size.
543 * We only need to do this for DISCOVER messages */
545 dlen
= 1 + 1 + 1 + 8;
546 switch(auth
->protocol
) {
547 case AUTH_PROTO_TOKEN
:
550 case AUTH_PROTO_DELAYEDREALM
:
552 dlen
+= t
->realm_len
;
554 case AUTH_PROTO_DELAYED
:
556 dlen
+= sizeof(t
->secretid
) + sizeof(hmac
);
559 return (ssize_t
)dlen
;
562 if (dlen
< 1 + 1 + 1 + 8) {
567 /* Ensure that d is inside m which *may* not be the case for DHPCPv4 */
568 if (data
< m
|| data
> m
+ mlen
|| data
+ dlen
> m
+ mlen
) {
573 /* Write out our option */
574 *data
++ = auth
->protocol
;
575 *data
++ = auth
->algorithm
;
578 case AUTH_RDM_MONOTONIC
:
579 rdm
= get_next_rdm_monotonic(auth
);
582 /* This block appeases gcc, clang doesn't need it */
583 rdm
= get_next_rdm_monotonic(auth
);
587 memcpy(data
, &rdm
, 8);
589 dlen
-= 1 + 1 + 1 + 8;
591 /* Special case as no hashing needs to be done. */
592 if (auth
->protocol
== AUTH_PROTO_TOKEN
) {
593 /* Should be impossible, but still */
598 if (dlen
< t
->key_len
) {
602 memcpy(data
, t
->key
, t
->key_len
);
603 return (ssize_t
)(dlen
- t
->key_len
);
606 /* DISCOVER or INFORM messages don't write auth info */
608 return (ssize_t
)dlen
;
610 /* Loading a saved lease without an authentication option */
614 /* Write out the Realm */
615 if (auth
->protocol
== AUTH_PROTO_DELAYEDREALM
) {
616 if (dlen
< t
->realm_len
) {
620 memcpy(data
, t
->realm
, t
->realm_len
);
621 data
+= t
->realm_len
;
622 dlen
-= t
->realm_len
;
625 /* Write out the SecretID */
626 if (auth
->protocol
== AUTH_PROTO_DELAYED
||
627 auth
->protocol
== AUTH_PROTO_DELAYEDREALM
)
629 if (dlen
< sizeof(t
->secretid
)) {
633 secretid
= htonl(t
->secretid
);
634 memcpy(data
, &secretid
, sizeof(secretid
));
635 data
+= sizeof(secretid
);
636 dlen
-= sizeof(secretid
);
639 /* Zero what's left, the MAC */
640 memset(data
, 0, dlen
);
642 /* RFC3318, section 5.2 - zero giaddr and hops */
644 p
= m
+ offsetof(struct dhcp_message
, hwopcount
);
647 p
= m
+ offsetof(struct dhcp_message
, giaddr
);
648 memcpy(&giaddr
, p
, sizeof(giaddr
));
649 memset(p
, 0, sizeof(giaddr
));
651 /* appease GCC again */
656 /* Create our hash and write it out */
657 switch(auth
->algorithm
) {
658 case AUTH_ALG_HMAC_MD5
:
659 hmac_md5(m
, mlen
, t
->key
, t
->key_len
, hmac
);
660 memcpy(data
, hmac
, sizeof(hmac
));
664 /* RFC3318, section 5.2 - restore giaddr and hops */
666 p
= m
+ offsetof(struct dhcp_message
, hwopcount
);
668 p
= m
+ offsetof(struct dhcp_message
, giaddr
);
669 memcpy(p
, &giaddr
, sizeof(giaddr
));
673 return (int)(dlen
- sizeof(hmac
)); /* should be zero */