1 /* SPDX-License-Identifier: BSD-2-Clause */
3 * dhcpcd - DHCP client daemon
4 * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 #if (BYTE_ORDER == LITTLE_ENDIAN)
57 #define htonll(x) ((uint64_t)htonl((uint32_t)((x) >> 32)) | \
58 (uint64_t)htonl((uint32_t)((x) & 0x00000000ffffffffULL)) << 32)
59 #else /* (BYTE_ORDER == LITTLE_ENDIAN) */
65 #if (BYTE_ORDER == LITTLE_ENDIAN)
66 #define ntohll(x) ((uint64_t)ntohl((uint32_t)((x) >> 32)) | \
67 (uint64_t)ntohl((uint32_t)((x) & 0x00000000ffffffffULL)) << 32)
68 #else /* (BYTE_ORDER == LITTLE_ENDIAN) */
73 #define HMAC_LENGTH 16
76 dhcp_auth_reset(struct authstate
*state
)
81 free(state
->token
->key
);
82 free(state
->token
->realm
);
87 free(state
->reconf
->key
);
88 free(state
->reconf
->realm
);
95 * Authenticate a DHCP message.
96 * m and mlen refer to the whole message.
97 * t is the DHCP type, pass it 4 or 6.
98 * data and dlen refer to the authentication option within the message.
101 dhcp_auth_validate(struct authstate
*state
, const struct auth
*auth
,
102 const void *vm
, size_t mlen
, int mp
, int mt
,
103 const void *vdata
, size_t dlen
)
105 const uint8_t *m
, *data
;
106 uint8_t protocol
, algorithm
, rdm
, *mm
, type
;
109 const uint8_t *d
, *realm
;
111 const struct token
*t
;
113 uint8_t hmac_code
[HMAC_LENGTH
];
115 if (dlen
< 3 + sizeof(replay
)) {
122 /* Ensure that d is inside m which *may* not be the case for DHCPv4.
123 * This can occur if the authentication option is split using
124 * DHCP long option from RFC 3399. Section 9 which does infact note that
125 * implementations should take this into account.
126 * Fixing this would be problematic, patches welcome. */
127 if (data
< m
|| data
> m
+ mlen
|| data
+ dlen
> m
+ mlen
) {
136 if (!(auth
->options
& DHCPCD_AUTH_SEND
)) {
137 /* If we didn't send any authorisation, it can only be a
139 if (protocol
!= AUTH_PROTO_RECONFKEY
) {
143 } else if (protocol
!= auth
->protocol
||
144 algorithm
!= auth
->algorithm
||
147 /* As we don't require authentication, we should still
148 * accept a reconfigure key */
149 if (protocol
!= AUTH_PROTO_RECONFKEY
||
150 auth
->options
& DHCPCD_AUTH_REQUIRE
)
158 memcpy(&replay
, d
, sizeof(replay
));
159 replay
= ntohll(replay
);
161 * Test for a replay attack.
163 * NOTE: Some servers always send a replay data value of zero.
164 * This is strictly compliant with RFC 3315 and 3318 which say:
165 * "If the RDM field contains 0x00, the replay detection field MUST be
166 * set to the value of a monotonically increasing counter."
167 * An example of a monotonically increasing sequence is:
168 * 1, 2, 2, 2, 2, 2, 2
169 * Errata 3474 updates RFC 3318 to say:
170 * "If the RDM field contains 0x00, the replay detection field MUST be
171 * set to the value of a strictly increasing counter."
173 * Taking the above into account, dhcpcd will only test for
174 * strictly speaking replay attacks if it receives any non zero
175 * replay data to validate against.
177 if (state
->token
&& state
->replay
!= 0) {
178 if (state
->replay
== (replay
^ 0x8000000000000000ULL
)) {
179 /* We don't know if the singular point is increasing
184 if ((uint64_t)(replay
- state
->replay
) <= 0) {
185 /* Replay attack detected */
191 dlen
-= sizeof(replay
);
196 /* Extract realm and secret.
197 * Rest of data is MAC. */
199 case AUTH_PROTO_TOKEN
:
200 secretid
= auth
->token_rcv_secretid
;
202 case AUTH_PROTO_DELAYED
:
203 if (dlen
< sizeof(secretid
) + sizeof(hmac_code
)) {
207 memcpy(&secretid
, d
, sizeof(secretid
));
208 secretid
= ntohl(secretid
);
209 d
+= sizeof(secretid
);
210 dlen
-= sizeof(secretid
);
212 case AUTH_PROTO_DELAYEDREALM
:
213 if (dlen
< sizeof(secretid
) + sizeof(hmac_code
)) {
217 realm_len
= dlen
- (sizeof(secretid
) + sizeof(hmac_code
));
223 memcpy(&secretid
, d
, sizeof(secretid
));
224 secretid
= ntohl(secretid
);
225 d
+= sizeof(secretid
);
226 dlen
-= sizeof(secretid
);
228 case AUTH_PROTO_RECONFKEY
:
229 if (dlen
!= 1 + 16) {
237 if ((mp
== 4 && mt
== DHCP_ACK
) ||
238 (mp
== 6 && mt
== DHCP6_REPLY
))
240 if (state
->reconf
== NULL
) {
242 malloc(sizeof(*state
->reconf
));
243 if (state
->reconf
== NULL
)
245 state
->reconf
->key
= malloc(16);
246 if (state
->reconf
->key
== NULL
) {
248 state
->reconf
= NULL
;
251 state
->reconf
->secretid
= 0;
252 state
->reconf
->expire
= 0;
253 state
->reconf
->realm
= NULL
;
254 state
->reconf
->realm_len
= 0;
255 state
->reconf
->key_len
= 16;
257 memcpy(state
->reconf
->key
, d
, 16);
262 if (state
->reconf
== NULL
)
264 /* Free the old token so we log acceptance */
269 /* Nothing to validate, just accepting the key */
270 return state
->reconf
;
272 if (!((mp
== 4 && mt
== DHCP_FORCERENEW
) ||
273 (mp
== 6 && mt
== DHCP6_RECONFIGURE
)))
278 if (state
->reconf
== NULL
) {
293 /* Find a token for the realm and secret */
294 TAILQ_FOREACH(t
, &auth
->tokens
, next
) {
295 if (t
->secretid
== secretid
&&
296 t
->realm_len
== realm_len
&&
297 (t
->realm_len
== 0 ||
298 memcmp(t
->realm
, realm
, t
->realm_len
) == 0))
306 if (time(&now
) == -1)
308 if (t
->expire
< now
) {
315 /* First message from the server */
317 (state
->token
->secretid
!= t
->secretid
||
318 state
->token
->realm_len
!= t
->realm_len
||
319 memcmp(state
->token
->realm
, t
->realm
, t
->realm_len
)))
325 /* Special case as no hashing needs to be done. */
326 if (protocol
== AUTH_PROTO_TOKEN
) {
327 if (dlen
!= t
->key_len
|| memcmp(d
, t
->key
, dlen
)) {
334 /* Make a duplicate of the message, but zero out the MAC part */
339 memset(mm
+ (d
- m
), 0, dlen
);
341 /* RFC3318, section 5.2 - zero giaddr and hops */
343 /* Assert the bootp structure is correct size. */
344 __CTASSERT(sizeof(struct bootp
) == 300);
346 *(mm
+ offsetof(struct bootp
, hops
)) = '\0';
347 memset(mm
+ offsetof(struct bootp
, giaddr
), 0, 4);
350 memset(hmac_code
, 0, sizeof(hmac_code
));
352 case AUTH_ALG_HMAC_MD5
:
353 hmac("md5", t
->key
, t
->key_len
, mm
, mlen
,
354 hmac_code
, sizeof(hmac_code
));
363 if (!consttime_memequal(d
, &hmac_code
, dlen
)) {
369 /* If we got here then authentication passed */
370 state
->replay
= replay
;
371 if (state
->token
== NULL
) {
372 /* We cannot just save a pointer because a reconfigure will
373 * recreate the token list. So we duplicate it. */
374 state
->token
= malloc(sizeof(*state
->token
));
376 state
->token
->secretid
= t
->secretid
;
377 state
->token
->key
= malloc(t
->key_len
);
378 if (state
->token
->key
) {
379 state
->token
->key_len
= t
->key_len
;
380 memcpy(state
->token
->key
, t
->key
, t
->key_len
);
387 state
->token
->realm
= malloc(t
->realm_len
);
388 if (state
->token
->realm
) {
389 state
->token
->realm_len
= t
->realm_len
;
390 memcpy(state
->token
->realm
, t
->realm
,
393 free(state
->token
->key
);
399 state
->token
->realm
= NULL
;
400 state
->token
->realm_len
= 0;
403 /* If we cannot save the token, we must invalidate */
404 if (state
->token
== NULL
)
412 get_next_rdm_monotonic_counter(struct auth
*auth
)
420 fp
= fopen(RDM_MONOFILE
, "r+");
423 return ++auth
->last_replay
; /* report error? */
424 fp
= fopen(RDM_MONOFILE
, "w");
426 return ++auth
->last_replay
; /* report error? */
428 flocked
= flock(fileno(fp
), LOCK_EX
);
433 flocked
= flock(fileno(fp
), LOCK_EX
);
435 if (fscanf(fp
, "0x%016" PRIu64
, &rdm
) != 1)
436 rdm
= 0; /* truncated? report error? */
440 if (fseek(fp
, 0, SEEK_SET
) == -1 ||
441 ftruncate(fileno(fp
), 0) == -1 ||
442 fprintf(fp
, "0x%016" PRIu64
"\n", rdm
) != 19 ||
445 if (!auth
->last_replay_set
) {
446 auth
->last_replay
= rdm
;
447 auth
->last_replay_set
= 1;
449 rdm
= ++auth
->last_replay
;
454 flock(fileno(fp
), LOCK_UN
);
460 #define NTP_EPOCH 2208988800U /* 1970 - 1900 in seconds */
461 #define NTP_SCALE_FRAC 4294967295.0 /* max value of the fractional part */
463 get_next_rdm_monotonic_clock(struct auth
*auth
)
469 if (clock_gettime(CLOCK_REALTIME
, &ts
) != 0)
470 return ++auth
->last_replay
; /* report error? */
472 secs
= (uint64_t)ts
.tv_sec
+ NTP_EPOCH
;
473 frac
= ((double)ts
.tv_nsec
/ 1e9
* NTP_SCALE_FRAC
);
474 rdm
= (secs
<< 32) | (uint64_t)frac
;
479 get_next_rdm_monotonic(struct auth
*auth
)
482 if (auth
->options
& DHCPCD_AUTH_RDM_COUNTER
)
483 return get_next_rdm_monotonic_counter(auth
);
484 return get_next_rdm_monotonic_clock(auth
);
488 * Encode a DHCP message.
489 * Either we know which token to use from the server response
490 * or we are using a basic configuration token.
491 * token is the token to encrypt with.
492 * m and mlen refer to the whole message.
493 * mp is the DHCP type, pass it 4 or 6.
494 * mt is the DHCP message type.
495 * data and dlen refer to the authentication option within the message.
498 dhcp_auth_encode(struct auth
*auth
, const struct token
*t
,
499 void *vm
, size_t mlen
, int mp
, int mt
,
500 void *vdata
, size_t dlen
)
503 uint8_t hmac_code
[HMAC_LENGTH
];
505 uint8_t hops
, *p
, *m
, *data
;
506 uint32_t giaddr
, secretid
;
509 /* Ignore the token argument given to us - always send using the
510 * configured token. */
511 if (auth
->protocol
== AUTH_PROTO_TOKEN
) {
512 TAILQ_FOREACH(t
, &auth
->tokens
, next
) {
513 if (t
->secretid
== auth
->token_snd_secretid
)
521 if (time(&now
) == -1)
523 if (t
->expire
< now
) {
530 switch(auth
->protocol
) {
531 case AUTH_PROTO_TOKEN
:
532 case AUTH_PROTO_DELAYED
:
533 case AUTH_PROTO_DELAYEDREALM
:
534 /* We don't ever send a reconf key */
541 switch(auth
->algorithm
) {
543 case AUTH_ALG_HMAC_MD5
:
551 case AUTH_RDM_MONOTONIC
:
558 /* DISCOVER or INFORM messages don't write auth info */
559 if ((mp
== 4 && (mt
== DHCP_DISCOVER
|| mt
== DHCP_INFORM
)) ||
560 (mp
== 6 && (mt
== DHCP6_SOLICIT
|| mt
== DHCP6_INFORMATION_REQ
)))
565 /* Work out the auth area size.
566 * We only need to do this for DISCOVER messages */
568 dlen
= 1 + 1 + 1 + 8;
569 switch(auth
->protocol
) {
570 case AUTH_PROTO_TOKEN
:
573 case AUTH_PROTO_DELAYEDREALM
:
575 dlen
+= t
->realm_len
;
577 case AUTH_PROTO_DELAYED
:
579 dlen
+= sizeof(t
->secretid
) + sizeof(hmac_code
);
582 return (ssize_t
)dlen
;
585 if (dlen
< 1 + 1 + 1 + 8) {
590 /* Ensure that d is inside m which *may* not be the case for DHPCPv4 */
593 if (data
< m
|| data
> m
+ mlen
|| data
+ dlen
> m
+ mlen
) {
598 /* Write out our option */
599 *data
++ = auth
->protocol
;
600 *data
++ = auth
->algorithm
;
602 * RFC 3315 21.4.4.1 says that SOLICIT in DELAYED authentication
603 * should not set RDM or it's data.
604 * An expired draft draft-ietf-dhc-dhcpv6-clarify-auth-01 suggets
605 * this should not be set for INFORMATION REQ messages as well,
606 * which is probably a good idea because both states start from zero.
609 !(auth
->protocol
& (AUTH_PROTO_DELAYED
| AUTH_PROTO_DELAYEDREALM
)))
613 case AUTH_RDM_MONOTONIC
:
614 rdm
= get_next_rdm_monotonic(auth
);
617 /* This block appeases gcc, clang doesn't need it */
618 rdm
= get_next_rdm_monotonic(auth
);
622 memcpy(data
, &rdm
, 8);
624 *data
++ = 0; /* rdm */
625 memset(data
, 0, 8); /* replay detection data */
628 dlen
-= 1 + 1 + 1 + 8;
630 /* Special case as no hashing needs to be done. */
631 if (auth
->protocol
== AUTH_PROTO_TOKEN
) {
632 /* Should be impossible, but still */
637 if (dlen
< t
->key_len
) {
641 memcpy(data
, t
->key
, t
->key_len
);
642 return (ssize_t
)(dlen
- t
->key_len
);
645 /* DISCOVER or INFORM messages don't write auth info */
647 return (ssize_t
)dlen
;
649 /* Loading a saved lease without an authentication option */
653 /* Write out the Realm */
654 if (auth
->protocol
== AUTH_PROTO_DELAYEDREALM
) {
655 if (dlen
< t
->realm_len
) {
659 memcpy(data
, t
->realm
, t
->realm_len
);
660 data
+= t
->realm_len
;
661 dlen
-= t
->realm_len
;
664 /* Write out the SecretID */
665 if (auth
->protocol
== AUTH_PROTO_DELAYED
||
666 auth
->protocol
== AUTH_PROTO_DELAYEDREALM
)
668 if (dlen
< sizeof(t
->secretid
)) {
672 secretid
= htonl(t
->secretid
);
673 memcpy(data
, &secretid
, sizeof(secretid
));
674 data
+= sizeof(secretid
);
675 dlen
-= sizeof(secretid
);
678 /* Zero what's left, the MAC */
679 memset(data
, 0, dlen
);
681 /* RFC3318, section 5.2 - zero giaddr and hops */
683 p
= m
+ offsetof(struct bootp
, hops
);
686 p
= m
+ offsetof(struct bootp
, giaddr
);
687 memcpy(&giaddr
, p
, sizeof(giaddr
));
688 memset(p
, 0, sizeof(giaddr
));
690 /* appease GCC again */
695 /* Create our hash and write it out */
696 switch(auth
->algorithm
) {
697 case AUTH_ALG_HMAC_MD5
:
698 hmac("md5", t
->key
, t
->key_len
, m
, mlen
,
699 hmac_code
, sizeof(hmac_code
));
700 memcpy(data
, hmac_code
, sizeof(hmac_code
));
704 /* RFC3318, section 5.2 - restore giaddr and hops */
706 p
= m
+ offsetof(struct bootp
, hops
);
708 p
= m
+ offsetof(struct bootp
, giaddr
);
709 memcpy(p
, &giaddr
, sizeof(giaddr
));
713 return (int)(dlen
- sizeof(hmac_code
)); /* should be zero */