Import dhcpcd-8.0.4 to vendor branch.
[dragonfly.git] / contrib / dhcpcd / src / auth.c
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3  * dhcpcd - DHCP client daemon
4  * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
5  * All rights reserved
6
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
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.
15  *
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
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/file.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <inttypes.h>
33 #include <stddef.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <time.h>
38 #include <unistd.h>
39
40 #include "config.h"
41 #include "auth.h"
42 #include "dhcp.h"
43 #include "dhcp6.h"
44 #include "dhcpcd.h"
45
46 #ifdef HAVE_HMAC_H
47 #include <hmac.h>
48 #endif
49
50 #ifdef __sun
51 #define htonll
52 #define ntohll
53 #endif
54
55 #ifndef htonll
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) */
60 #define htonll(x)       (x)
61 #endif
62 #endif  /* htonll */
63
64 #ifndef ntohll
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) */
69 #define ntohll(x)       (x)
70 #endif
71 #endif  /* ntohll */
72
73 #define HMAC_LENGTH     16
74
75 void
76 dhcp_auth_reset(struct authstate *state)
77 {
78
79         state->replay = 0;
80         if (state->token) {
81                 free(state->token->key);
82                 free(state->token->realm);
83                 free(state->token);
84                 state->token = NULL;
85         }
86         if (state->reconf) {
87                 free(state->reconf->key);
88                 free(state->reconf->realm);
89                 free(state->reconf);
90                 state->reconf = NULL;
91         }
92 }
93
94 /*
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.
99  */
100 const struct token *
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)
104 {
105         const uint8_t *m, *data;
106         uint8_t protocol, algorithm, rdm, *mm, type;
107         uint64_t replay;
108         uint32_t secretid;
109         const uint8_t *d, *realm;
110         size_t realm_len;
111         const struct token *t;
112         time_t now;
113         uint8_t hmac_code[HMAC_LENGTH];
114
115         if (dlen < 3 + sizeof(replay)) {
116                 errno = EINVAL;
117                 return NULL;
118         }
119
120         m = vm;
121         data = vdata;
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) {
128                 errno = ERANGE;
129                 return NULL;
130         }
131
132         d = data;
133         protocol = *d++;
134         algorithm = *d++;
135         rdm = *d++;
136         if (!(auth->options & DHCPCD_AUTH_SEND)) {
137                 /* If we didn't send any authorisation, it can only be a
138                  * reconfigure key */
139                 if (protocol != AUTH_PROTO_RECONFKEY) {
140                         errno = EINVAL;
141                         return NULL;
142                 }
143         } else if (protocol != auth->protocol ||
144                     algorithm != auth->algorithm ||
145                     rdm != auth->rdm)
146         {
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)
151                 {
152                         errno = EPERM;
153                         return NULL;
154                 }
155         }
156         dlen -= 3;
157
158         memcpy(&replay, d, sizeof(replay));
159         replay = ntohll(replay);
160         /*
161          * Test for a replay attack.
162          *
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."
172          *
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.
176          */
177         if (state->token && state->replay != 0) {
178                 if (state->replay == (replay ^ 0x8000000000000000ULL)) {
179                         /* We don't know if the singular point is increasing
180                          * or decreasing. */
181                         errno = EPERM;
182                         return NULL;
183                 }
184                 if ((uint64_t)(replay - state->replay) <= 0) {
185                         /* Replay attack detected */
186                         errno = EPERM;
187                         return NULL;
188                 }
189         }
190         d+= sizeof(replay);
191         dlen -= sizeof(replay);
192
193         realm = NULL;
194         realm_len = 0;
195
196         /* Extract realm and secret.
197          * Rest of data is MAC. */
198         switch (protocol) {
199         case AUTH_PROTO_TOKEN:
200                 secretid = auth->token_rcv_secretid;
201                 break;
202         case AUTH_PROTO_DELAYED:
203                 if (dlen < sizeof(secretid) + sizeof(hmac_code)) {
204                         errno = EINVAL;
205                         return NULL;
206                 }
207                 memcpy(&secretid, d, sizeof(secretid));
208                 secretid = ntohl(secretid);
209                 d += sizeof(secretid);
210                 dlen -= sizeof(secretid);
211                 break;
212         case AUTH_PROTO_DELAYEDREALM:
213                 if (dlen < sizeof(secretid) + sizeof(hmac_code)) {
214                         errno = EINVAL;
215                         return NULL;
216                 }
217                 realm_len = dlen - (sizeof(secretid) + sizeof(hmac_code));
218                 if (realm_len) {
219                         realm = d;
220                         d += realm_len;
221                         dlen -= realm_len;
222                 }
223                 memcpy(&secretid, d, sizeof(secretid));
224                 secretid = ntohl(secretid);
225                 d += sizeof(secretid);
226                 dlen -= sizeof(secretid);
227                 break;
228         case AUTH_PROTO_RECONFKEY:
229                 if (dlen != 1 + 16) {
230                         errno = EINVAL;
231                         return NULL;
232                 }
233                 type = *d++;
234                 dlen--;
235                 switch (type) {
236                 case 1:
237                         if ((mp == 4 && mt == DHCP_ACK) ||
238                             (mp == 6 && mt == DHCP6_REPLY))
239                         {
240                                 if (state->reconf == NULL) {
241                                         state->reconf =
242                                             malloc(sizeof(*state->reconf));
243                                         if (state->reconf == NULL)
244                                                 return NULL;
245                                         state->reconf->key = malloc(16);
246                                         if (state->reconf->key == NULL) {
247                                                 free(state->reconf);
248                                                 state->reconf = NULL;
249                                                 return NULL;
250                                         }
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;
256                                 }
257                                 memcpy(state->reconf->key, d, 16);
258                         } else {
259                                 errno = EINVAL;
260                                 return NULL;
261                         }
262                         if (state->reconf == NULL)
263                                 errno = ENOENT;
264                         /* Free the old token so we log acceptance */
265                         if (state->token) {
266                                 free(state->token);
267                                 state->token = NULL;
268                         }
269                         /* Nothing to validate, just accepting the key */
270                         return state->reconf;
271                 case 2:
272                         if (!((mp == 4 && mt == DHCP_FORCERENEW) ||
273                             (mp == 6 && mt == DHCP6_RECONFIGURE)))
274                         {
275                                 errno = EINVAL;
276                                 return NULL;
277                         }
278                         if (state->reconf == NULL) {
279                                 errno = ENOENT;
280                                 return NULL;
281                         }
282                         t = state->reconf;
283                         goto gottoken;
284                 default:
285                         errno = EINVAL;
286                         return NULL;
287                 }
288         default:
289                 errno = ENOTSUP;
290                 return NULL;
291         }
292
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))
299                         break;
300         }
301         if (t == NULL) {
302                 errno = ESRCH;
303                 return NULL;
304         }
305         if (t->expire) {
306                 if (time(&now) == -1)
307                         return NULL;
308                 if (t->expire < now) {
309                         errno = EFAULT;
310                         return NULL;
311                 }
312         }
313
314 gottoken:
315         /* First message from the server */
316         if (state->token &&
317             (state->token->secretid != t->secretid ||
318             state->token->realm_len != t->realm_len ||
319             memcmp(state->token->realm, t->realm, t->realm_len)))
320         {
321                 errno = EPERM;
322                 return NULL;
323         }
324
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)) {
328                         errno = EPERM;
329                         return NULL;
330                 }
331                 goto finish;
332         }
333
334         /* Make a duplicate of the message, but zero out the MAC part */
335         mm = malloc(mlen);
336         if (mm == NULL)
337                 return NULL;
338         memcpy(mm, m, mlen);
339         memset(mm + (d - m), 0, dlen);
340
341         /* RFC3318, section 5.2 - zero giaddr and hops */
342         if (mp == 4) {
343                 /* Assert the bootp structure is correct size. */
344                 __CTASSERT(sizeof(struct bootp) == 300);
345
346                 *(mm + offsetof(struct bootp, hops)) = '\0';
347                 memset(mm + offsetof(struct bootp, giaddr), 0, 4);
348         }
349
350         memset(hmac_code, 0, sizeof(hmac_code));
351         switch (algorithm) {
352         case AUTH_ALG_HMAC_MD5:
353                 hmac("md5", t->key, t->key_len, mm, mlen,
354                      hmac_code, sizeof(hmac_code));
355                 break;
356         default:
357                 errno = ENOSYS;
358                 free(mm);
359                 return NULL;
360         }
361
362         free(mm);
363         if (!consttime_memequal(d, &hmac_code, dlen)) {
364                 errno = EPERM;
365                 return NULL;
366         }
367
368 finish:
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));
375                 if (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);
381                         } else {
382                                 free(state->token);
383                                 state->token = NULL;
384                                 return NULL;
385                         }
386                         if (t->realm_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,
391                                             t->realm_len);
392                                 } else {
393                                         free(state->token->key);
394                                         free(state->token);
395                                         state->token = NULL;
396                                         return NULL;
397                                 }
398                         } else {
399                                 state->token->realm = NULL;
400                                 state->token->realm_len = 0;
401                         }
402                 }
403                 /* If we cannot save the token, we must invalidate */
404                 if (state->token == NULL)
405                         return NULL;
406         }
407
408         return t;
409 }
410
411 static uint64_t
412 get_next_rdm_monotonic_counter(struct auth *auth)
413 {
414         FILE *fp;
415         uint64_t rdm;
416 #ifdef LOCK_EX
417         int flocked;
418 #endif
419
420         fp = fopen(RDM_MONOFILE, "r+");
421         if (fp == NULL) {
422                 if (errno != ENOENT)
423                         return ++auth->last_replay; /* report error? */
424                 fp = fopen(RDM_MONOFILE, "w");
425                 if (fp == NULL)
426                         return ++auth->last_replay; /* report error? */
427 #ifdef LOCK_EX
428                 flocked = flock(fileno(fp), LOCK_EX);
429 #endif
430                 rdm = 0;
431         } else {
432 #ifdef LOCK_EX
433                 flocked = flock(fileno(fp), LOCK_EX);
434 #endif
435                 if (fscanf(fp, "0x%016" PRIu64, &rdm) != 1)
436                         rdm = 0; /* truncated? report error? */
437         }
438
439         rdm++;
440         if (fseek(fp, 0, SEEK_SET) == -1 ||
441             ftruncate(fileno(fp), 0) == -1 ||
442             fprintf(fp, "0x%016" PRIu64 "\n", rdm) != 19 ||
443             fflush(fp) == EOF)
444         {
445                 if (!auth->last_replay_set) {
446                         auth->last_replay = rdm;
447                         auth->last_replay_set = 1;
448                 } else
449                         rdm = ++auth->last_replay;
450                 /* report error? */
451         }
452 #ifdef LOCK_EX
453         if (flocked == 0)
454                 flock(fileno(fp), LOCK_UN);
455 #endif
456         fclose(fp);
457         return rdm;
458 }
459
460 #define NTP_EPOCH       2208988800U     /* 1970 - 1900 in seconds */
461 #define NTP_SCALE_FRAC  4294967295.0    /* max value of the fractional part */
462 static uint64_t
463 get_next_rdm_monotonic_clock(struct auth *auth)
464 {
465         struct timespec ts;
466         uint64_t secs, rdm;
467         double frac;
468
469         if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
470                 return ++auth->last_replay; /* report error? */
471
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;
475         return rdm;
476 }
477
478 static uint64_t
479 get_next_rdm_monotonic(struct auth *auth)
480 {
481
482         if (auth->options & DHCPCD_AUTH_RDM_COUNTER)
483                 return get_next_rdm_monotonic_counter(auth);
484         return get_next_rdm_monotonic_clock(auth);
485 }
486
487 /*
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.
496  */
497 ssize_t
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)
501 {
502         uint64_t rdm;
503         uint8_t hmac_code[HMAC_LENGTH];
504         time_t now;
505         uint8_t hops, *p, *m, *data;
506         uint32_t giaddr, secretid;
507         bool auth_info;
508
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)
514                                 break;
515                 }
516                 if (t == NULL) {
517                         errno = EINVAL;
518                         return -1;
519                 }
520                 if (t->expire) {
521                         if (time(&now) == -1)
522                                 return -1;
523                         if (t->expire < now) {
524                                 errno = EPERM;
525                                 return -1;
526                         }
527                 }
528         }
529
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 */
535                 break;
536         default:
537                 errno = ENOTSUP;
538                 return -1;
539         }
540
541         switch(auth->algorithm) {
542         case AUTH_ALG_NONE:
543         case AUTH_ALG_HMAC_MD5:
544                 break;
545         default:
546                 errno = ENOTSUP;
547                 return -1;
548         }
549
550         switch(auth->rdm) {
551         case AUTH_RDM_MONOTONIC:
552                 break;
553         default:
554                 errno = ENOTSUP;
555                 return -1;
556         }
557
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)))
561                 auth_info = false;
562         else
563                 auth_info = true;
564
565         /* Work out the auth area size.
566          * We only need to do this for DISCOVER messages */
567         if (vdata == NULL) {
568                 dlen = 1 + 1 + 1 + 8;
569                 switch(auth->protocol) {
570                 case AUTH_PROTO_TOKEN:
571                         dlen += t->key_len;
572                         break;
573                 case AUTH_PROTO_DELAYEDREALM:
574                         if (auth_info && t)
575                                 dlen += t->realm_len;
576                         /* FALLTHROUGH */
577                 case AUTH_PROTO_DELAYED:
578                         if (auth_info && t)
579                                 dlen += sizeof(t->secretid) + sizeof(hmac_code);
580                         break;
581                 }
582                 return (ssize_t)dlen;
583         }
584
585         if (dlen < 1 + 1 + 1 + 8) {
586                 errno = ENOBUFS;
587                 return -1;
588         }
589
590         /* Ensure that d is inside m which *may* not be the case for DHPCPv4 */
591         m = vm;
592         data = vdata;
593         if (data < m || data > m + mlen || data + dlen > m + mlen) {
594                 errno = ERANGE;
595                 return -1;
596         }
597
598         /* Write out our option */
599         *data++ = auth->protocol;
600         *data++ = auth->algorithm;
601         /*
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.
607          */
608         if (auth_info ||
609             !(auth->protocol & (AUTH_PROTO_DELAYED | AUTH_PROTO_DELAYEDREALM)))
610         {
611                 *data++ = auth->rdm;
612                 switch (auth->rdm) {
613                 case AUTH_RDM_MONOTONIC:
614                         rdm = get_next_rdm_monotonic(auth);
615                         break;
616                 default:
617                         /* This block appeases gcc, clang doesn't need it */
618                         rdm = get_next_rdm_monotonic(auth);
619                         break;
620                 }
621                 rdm = htonll(rdm);
622                 memcpy(data, &rdm, 8);
623         } else {
624                 *data++ = 0;            /* rdm */
625                 memset(data, 0, 8);     /* replay detection data */
626         }
627         data += 8;
628         dlen -= 1 + 1 + 1 + 8;
629
630         /* Special case as no hashing needs to be done. */
631         if (auth->protocol == AUTH_PROTO_TOKEN) {
632                 /* Should be impossible, but still */
633                 if (t == NULL) {
634                         errno = EINVAL;
635                         return -1;
636                 }
637                 if (dlen < t->key_len) {
638                         errno = ENOBUFS;
639                         return -1;
640                 }
641                 memcpy(data, t->key, t->key_len);
642                 return (ssize_t)(dlen - t->key_len);
643         }
644
645         /* DISCOVER or INFORM messages don't write auth info */
646         if (!auth_info)
647                 return (ssize_t)dlen;
648
649         /* Loading a saved lease without an authentication option */
650         if (t == NULL)
651                 return 0;
652
653         /* Write out the Realm */
654         if (auth->protocol == AUTH_PROTO_DELAYEDREALM) {
655                 if (dlen < t->realm_len) {
656                         errno = ENOBUFS;
657                         return -1;
658                 }
659                 memcpy(data, t->realm, t->realm_len);
660                 data += t->realm_len;
661                 dlen -= t->realm_len;
662         }
663
664         /* Write out the SecretID */
665         if (auth->protocol == AUTH_PROTO_DELAYED ||
666             auth->protocol == AUTH_PROTO_DELAYEDREALM)
667         {
668                 if (dlen < sizeof(t->secretid)) {
669                         errno = ENOBUFS;
670                         return -1;
671                 }
672                 secretid = htonl(t->secretid);
673                 memcpy(data, &secretid, sizeof(secretid));
674                 data += sizeof(secretid);
675                 dlen -= sizeof(secretid);
676         }
677
678         /* Zero what's left, the MAC */
679         memset(data, 0, dlen);
680
681         /* RFC3318, section 5.2 - zero giaddr and hops */
682         if (mp == 4) {
683                 p = m + offsetof(struct bootp, hops);
684                 hops = *p;
685                 *p = '\0';
686                 p = m + offsetof(struct bootp, giaddr);
687                 memcpy(&giaddr, p, sizeof(giaddr));
688                 memset(p, 0, sizeof(giaddr));
689         } else {
690                 /* appease GCC again */
691                 hops = 0;
692                 giaddr = 0;
693         }
694
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));
701                 break;
702         }
703
704         /* RFC3318, section 5.2 - restore giaddr and hops */
705         if (mp == 4) {
706                 p = m + offsetof(struct bootp, hops);
707                 *p = hops;
708                 p = m + offsetof(struct bootp, giaddr);
709                 memcpy(p, &giaddr, sizeof(giaddr));
710         }
711
712         /* Done! */
713         return (int)(dlen - sizeof(hmac_code)); /* should be zero */
714 }