1 /* Mode config related functions
2 * Copyright (C) 2001-2002 Colubris Networks
3 * Copyright (C) 2003 Sean Mathews - Nu Tech Software Solutions, inc.
4 * Copyright (C) 2003-2004 Xelerance Corporation
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * RCSID $Id: modecfg.c,v 1.6 2006/04/24 20:44:57 as Exp $
18 * This code originally written by Colubris Networks, Inc.
19 * Extraction of patch and porting to 1.99 codebases by Xelerance Corporation
20 * Porting to 2.x by Sean Mathews
29 #include "constants.h"
34 #include "ipsec_doi.h"
43 * Addresses assigned (usually via MODE_CONFIG) to the Initiator
53 * Get inside IP address for a connection
56 get_internal_addresses(struct connection
*c
, struct internal_addr
*ia
)
60 if (isanyaddr(&c
->spd
.that
.host_srcip
))
62 /* not defined in connection - fetch it from LDAP */
66 ia
->ipaddr
= c
->spd
.that
.host_srcip
;
71 * Compute HASH of Mode Config.
74 mode_cfg_hash(u_char
*dest
, const u_char
*start
, const u_char
*roof
75 , const struct state
*st
)
79 hmac_init_chunk(&ctx
, st
->st_oakley
.hasher
, st
->st_skeyid_a
);
80 hmac_update(&ctx
, (const u_char
*) &st
->st_msgid
, sizeof(st
->st_msgid
));
81 hmac_update(&ctx
, start
, roof
-start
);
82 hmac_final(dest
, &ctx
);
85 DBG_log("MODE CFG: HASH computed:");
86 DBG_dump("", dest
, ctx
.hmac_digest_size
)
88 return ctx
.hmac_digest_size
;
93 * Generates a reply stream containing Mode Config information (eg: IP, DNS, WINS)
95 stf_status
modecfg_resp(struct state
*st
102 u_char
*r_hash_start
,*r_hashval
;
104 /* START_HASH_PAYLOAD(rbody, ISAKMP_NEXT_ATTR); */
108 int np
= ISAKMP_NEXT_ATTR
;
110 if (!out_generic(np
, &isakmp_hash_desc
, rbody
, &hash_pbs
))
111 return STF_INTERNAL_ERROR
;
112 r_hashval
= hash_pbs
.cur
; /* remember where to plant value */
113 if (!out_zero(st
->st_oakley
.hasher
->hash_digest_size
, &hash_pbs
, "HASH"))
114 return STF_INTERNAL_ERROR
;
115 close_output_pbs(&hash_pbs
);
116 r_hash_start
= (rbody
)->cur
; /* hash from after HASH payload */
121 struct isakmp_mode_attr attrh
;
122 struct isakmp_attribute attr
;
123 pb_stream strattr
,attrval
;
125 struct internal_addr ia
;
126 int dns_idx
, wins_idx
;
129 attrh
.isama_np
= ISAKMP_NEXT_NONE
;
130 attrh
.isama_type
= replytype
;
132 attrh
.isama_identifier
= ap_id
;
133 if (!out_struct(&attrh
, &isakmp_attr_desc
, rbody
, &strattr
))
134 return STF_INTERNAL_ERROR
;
136 get_internal_addresses(st
->st_connection
, &ia
);
138 if (!isanyaddr(&ia
.dns
[0])) /* We got DNS addresses, answer with those */
139 resp
|= LELEM(INTERNAL_IP4_DNS
);
141 resp
&= ~LELEM(INTERNAL_IP4_DNS
);
143 if (!isanyaddr(&ia
.wins
[0])) /* We got WINS addresses, answer with those */
144 resp
|= LELEM(INTERNAL_IP4_NBNS
);
146 resp
&= ~LELEM(INTERNAL_IP4_NBNS
);
150 if (memcmp(&st
->st_connection
->spd
.that
.client
.addr
152 ,sizeof(ia
.ipaddr
)) != 0)
154 /* Make the Internal IP address and Netmask
155 * as that client address
157 st
->st_connection
->spd
.that
.client
.addr
= ia
.ipaddr
;
158 st
->st_connection
->spd
.that
.client
.maskbits
= 32;
159 st
->st_connection
->spd
.that
.has_client
= TRUE
;
169 dont_advance
= FALSE
;
172 const u_char
*byte_ptr
;
175 /* ISAKMP attr out */
176 attr
.isaat_af_type
= attr_type
| ISAKMP_ATTR_AF_TLV
;
177 out_struct(&attr
, &isakmp_modecfg_attribute_desc
, &strattr
, &attrval
);
181 case INTERNAL_IP4_ADDRESS
:
183 char srcip
[ADDRTOT_BUF
];
185 addrtot(&ia
.ipaddr
, 0, srcip
, sizeof(srcip
));
186 plog("assigning virtual IP source address %s", srcip
);
187 len
= addrbytesptr(&ia
.ipaddr
, &byte_ptr
);
188 out_raw(byte_ptr
,len
,&attrval
,"IP4_addr");
191 case INTERNAL_IP4_NETMASK
:
195 char mask
[4],bits
[8]={0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe};
196 int t
,m
=st
->st_connection
->that
.host_addr
.maskbit
;
206 if (st
->st_connection
->spd
.this.client
.maskbits
== 0)
209 mask
= 0xffffffff * 1;
210 out_raw(&mask
,4,&attrval
,"IP4_mask");
213 case INTERNAL_IP4_SUBNET
:
216 char bits
[8] = {0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe};
218 int m
= st
->st_connection
->spd
.this.client
.maskbits
;
220 for (t
= 0; t
< 4; t
++)
230 len
= addrbytesptr(&st
->st_connection
->spd
.this.client
.addr
, &byte_ptr
);
231 out_raw(byte_ptr
,len
,&attrval
,"IP4_subnet");
232 out_raw(mask
,sizeof(mask
),&attrval
,"IP4_submsk");
235 case INTERNAL_IP4_DNS
:
236 len
= addrbytesptr(&ia
.dns
[dns_idx
++], &byte_ptr
);
237 out_raw(byte_ptr
,len
,&attrval
,"IP4_dns");
238 if (dns_idx
< 2 && !isanyaddr(&ia
.dns
[dns_idx
]))
243 case INTERNAL_IP4_NBNS
:
244 len
= addrbytesptr(&ia
.wins
[wins_idx
++], &byte_ptr
);
245 out_raw(byte_ptr
,len
,&attrval
,"IP4_wins");
246 if (wins_idx
< 2 && !isanyaddr(&ia
.wins
[wins_idx
]))
252 plog("attempt to send unsupported mode cfg attribute %s."
253 , enum_show(&modecfg_attr_names
, attr_type
));
256 close_output_pbs(&attrval
);
265 close_message(&strattr
);
268 mode_cfg_hash(r_hashval
,r_hash_start
,rbody
->cur
,st
);
269 close_message(rbody
);
270 encrypt_message(rbody
, st
);
274 /* Set MODE_CONFIG data to client.
275 * Pack IP Addresses, DNS, etc... and ship
277 stf_status
modecfg_send_set(struct state
*st
)
279 pb_stream reply
,rbody
;
283 init_pbs(&reply
, buf
, sizeof(buf
), "ModecfgR1");
285 st
->st_state
= STATE_MODE_CFG_R1
;
288 struct isakmp_hdr hdr
;
290 zero(&hdr
); /* default to 0 */
291 hdr
.isa_version
= ISAKMP_MAJOR_VERSION
<< ISA_MAJ_SHIFT
| ISAKMP_MINOR_VERSION
;
292 hdr
.isa_np
= ISAKMP_NEXT_HASH
;
293 hdr
.isa_xchg
= ISAKMP_XCHG_MODE_CFG
;
294 hdr
.isa_flags
= ISAKMP_FLAG_ENCRYPTION
;
295 memcpy(hdr
.isa_icookie
, st
->st_icookie
, COOKIE_SIZE
);
296 memcpy(hdr
.isa_rcookie
, st
->st_rcookie
, COOKIE_SIZE
);
297 hdr
.isa_msgid
= st
->st_msgid
;
299 if (!out_struct(&hdr
, &isakmp_hdr_desc
, &reply
, &rbody
))
301 return STF_INTERNAL_ERROR
;
305 #define MODECFG_SET_ITEM ( LELEM(INTERNAL_IP4_ADDRESS) | LELEM(INTERNAL_IP4_SUBNET) | LELEM(INTERNAL_IP4_NBNS) | LELEM(INTERNAL_IP4_DNS) )
307 modecfg_resp(st
, MODECFG_SET_ITEM
312 #undef MODECFG_SET_ITEM
314 clonetochunk(st
->st_tpacket
, reply
.start
315 , pbs_offset(&reply
), "ModeCfg set");
318 send_packet(st
, "ModeCfg set");
320 /* RETRANSMIT if Main, SA_REPLACE if Aggressive */
321 if (st
->st_event
->ev_type
!= EVENT_RETRANSMIT
322 && st
->st_event
->ev_type
!= EVENT_NULL
)
325 event_schedule(EVENT_RETRANSMIT
, EVENT_RETRANSMIT_DELAY_0
, st
);
331 /* Set MODE_CONFIG data to client.
332 * Pack IP Addresses, DNS, etc... and ship
335 modecfg_start_set(struct state
*st
)
337 if (st
->st_msgid
== 0)
339 /* pick a new message id */
340 st
->st_msgid
= generate_msgid(st
);
342 st
->st_modecfg
.vars_set
= TRUE
;
344 return modecfg_send_set(st
);
348 * Send modecfg IP address request (IP4 address)
351 modecfg_send_request(struct state
*st
)
356 u_char
*r_hash_start
,*r_hashval
;
359 init_pbs(&reply
, buf
, sizeof(buf
), "modecfg_buf");
361 plog("sending ModeCfg request");
363 /* this is the beginning of a new exchange */
364 st
->st_msgid
= generate_msgid(st
);
365 st
->st_state
= STATE_MODE_CFG_I1
;
369 struct isakmp_hdr hdr
;
371 zero(&hdr
); /* default to 0 */
372 hdr
.isa_version
= ISAKMP_MAJOR_VERSION
<< ISA_MAJ_SHIFT
| ISAKMP_MINOR_VERSION
;
373 hdr
.isa_np
= ISAKMP_NEXT_HASH
;
374 hdr
.isa_xchg
= ISAKMP_XCHG_MODE_CFG
;
375 hdr
.isa_flags
= ISAKMP_FLAG_ENCRYPTION
;
376 memcpy(hdr
.isa_icookie
, st
->st_icookie
, COOKIE_SIZE
);
377 memcpy(hdr
.isa_rcookie
, st
->st_rcookie
, COOKIE_SIZE
);
378 hdr
.isa_msgid
= st
->st_msgid
;
380 if (!out_struct(&hdr
, &isakmp_hdr_desc
, &reply
, &rbody
))
382 return STF_INTERNAL_ERROR
;
386 START_HASH_PAYLOAD(rbody
, ISAKMP_NEXT_ATTR
);
390 struct isakmp_mode_attr attrh
;
391 struct isakmp_attribute attr
;
394 attrh
.isama_np
= ISAKMP_NEXT_NONE
;
395 attrh
.isama_type
= ISAKMP_CFG_REQUEST
;
396 attrh
.isama_identifier
= 0;
397 if (!out_struct(&attrh
, &isakmp_attr_desc
, &rbody
, &strattr
))
398 return STF_INTERNAL_ERROR
;
399 /* ISAKMP attr out (ipv4) */
400 attr
.isaat_af_type
= INTERNAL_IP4_ADDRESS
;
402 out_struct(&attr
, &isakmp_modecfg_attribute_desc
, &strattr
, NULL
);
404 /* ISAKMP attr out (netmask) */
405 attr
.isaat_af_type
= INTERNAL_IP4_NETMASK
;
407 out_struct(&attr
, &isakmp_modecfg_attribute_desc
, &strattr
, NULL
);
409 close_message(&strattr
);
412 mode_cfg_hash(r_hashval
,r_hash_start
,rbody
.cur
,st
);
414 close_message(&rbody
);
415 close_output_pbs(&reply
);
417 init_phase2_iv(st
, &st
->st_msgid
);
418 encrypt_message(&rbody
, st
);
420 clonetochunk(st
->st_tpacket
, reply
.start
, pbs_offset(&reply
)
424 send_packet(st
, "modecfg: req");
426 /* RETRANSMIT if Main, SA_REPLACE if Aggressive */
427 if (st
->st_event
->ev_type
!= EVENT_RETRANSMIT
)
430 event_schedule(EVENT_RETRANSMIT
, EVENT_RETRANSMIT_DELAY_0
* 3, st
);
432 st
->st_modecfg
.started
= TRUE
;
438 * parse a modecfg attribute payload
441 modecfg_parse_attributes(pb_stream
*attrs
, u_int
*set
)
443 struct isakmp_attribute attr
;
446 while (pbs_left(attrs
) > sizeof(struct isakmp_attribute
))
448 if (!in_struct(&attr
, &isakmp_modecfg_attribute_desc
, attrs
, &strattr
))
450 int len
= (attr
.isaat_af_type
& 0x8000)? 4 : attr
.isaat_lv
;
454 plog("Attribute was too short: %d", len
);
461 switch (attr
.isaat_af_type
& ISAKMP_ATTR_RTYPE_MASK
)
463 case INTERNAL_IP4_ADDRESS
:
464 case INTERNAL_IP4_NETMASK
:
465 case INTERNAL_IP4_DNS
:
466 case INTERNAL_IP4_SUBNET
:
467 case INTERNAL_IP4_NBNS
:
468 *set
|= LELEM(attr
.isaat_af_type
);
471 plog("unsupported mode cfg attribute %s received."
472 , enum_show(&modecfg_attr_names
473 , attr
.isaat_af_type
& ISAKMP_ATTR_RTYPE_MASK
));
480 /* STATE_MODE_CFG_R0:
481 * HDR*, HASH, ATTR(REQ=IP) --> HDR*, HASH, ATTR(REPLY=IP)
483 * This state occurs both in the responder and in the initiator.
485 * In the responding server, it occurs when the client *asks* for an IP
486 * address or other information.
488 * Otherwise, it occurs in the initiator when the server sends a challenge
489 * a set, or has a reply to our request.
492 modecfg_inR0(struct msg_digest
*md
)
494 struct state
*const st
= md
->st
;
495 struct payload_digest
*p
;
498 plog("received ModeCfg request");
500 st
->st_msgid
= md
->hdr
.isa_msgid
;
501 CHECK_QUICK_HASH(md
, mode_cfg_hash(hash_val
503 , md
->message_pbs
.roof
, st
)
504 , "MODECFG-HASH", "MODE R0");
506 /* process the MODECFG payloads therein */
507 for (p
= md
->chain
[ISAKMP_NEXT_ATTR
]; p
!= NULL
; p
= p
->next
)
509 u_int set_modecfg_attrs
= LEMPTY
;
511 switch (p
->payload
.attribute
.isama_type
)
514 plog("Expecting ISAKMP_CFG_REQUEST, got %s instead (ignored)."
515 , enum_name(&attr_msg_type_names
516 , p
->payload
.attribute
.isama_type
));
518 stat
= modecfg_parse_attributes(&p
->pbs
, &set_modecfg_attrs
);
523 case ISAKMP_CFG_REQUEST
:
524 stat
= modecfg_parse_attributes(&p
->pbs
, &set_modecfg_attrs
);
528 stat
= modecfg_resp(st
, set_modecfg_attrs
532 ,p
->payload
.attribute
.isama_identifier
);
536 /* notification payload - not exactly the right choice, but okay */
537 md
->note
= CERTIFICATE_UNAVAILABLE
;
541 /* they asked us, we responded, msgid is done */
548 /* STATE_MODE_CFG_R2:
549 * HDR*, HASH, ATTR(SET=IP) --> HDR*, HASH, ATTR(ACK,OK)
551 * used in server push mode, on the client (initiator).
554 modecfg_inI2(struct msg_digest
*md
)
556 struct state
*const st
= md
->st
;
557 pb_stream
*attrs
= &md
->chain
[ISAKMP_NEXT_ATTR
]->pbs
;
560 struct payload_digest
*p
;
561 u_int16_t isama_id
= 0;
563 st
->st_msgid
= md
->hdr
.isa_msgid
;
565 , mode_cfg_hash(hash_val
567 , md
->message_pbs
.roof
569 , "MODECFG-HASH", "MODE R1");
571 for (p
= md
->chain
[ISAKMP_NEXT_ATTR
]; p
!= NULL
; p
= p
->next
)
573 struct isakmp_attribute attr
;
576 isama_id
= p
->payload
.attribute
.isama_identifier
;
578 if (p
->payload
.attribute
.isama_type
!= ISAKMP_CFG_SET
)
580 plog("Expecting MODE_CFG_SET, got %x instead."
581 ,md
->chain
[ISAKMP_NEXT_ATTR
]->payload
.attribute
.isama_type
);
585 /* CHECK that SET has been received. */
587 while (pbs_left(attrs
) > sizeof(struct isakmp_attribute
))
589 if (!in_struct(&attr
, &isakmp_modecfg_attribute_desc
595 if (attr
.isaat_af_type
& 0x8000)
602 plog("Attribute was too short: %d", len
);
609 switch (attr
.isaat_af_type
& ISAKMP_ATTR_RTYPE_MASK
)
611 case INTERNAL_IP4_ADDRESS
:
613 struct connection
*c
= st
->st_connection
;
615 u_int32_t
*ap
= (u_int32_t
*)(strattr
.cur
);
616 a
.u
.v4
.sin_family
= AF_INET
;
618 memcpy(&a
.u
.v4
.sin_addr
.s_addr
, ap
619 , sizeof(a
.u
.v4
.sin_addr
.s_addr
));
621 if (addrbytesptr(&c
->spd
.this.host_srcip
, NULL
) == 0
622 || isanyaddr(&c
->spd
.this.host_srcip
))
624 char srcip
[ADDRTOT_BUF
];
626 c
->spd
.this.host_srcip
= a
;
627 addrtot(&a
, 0, srcip
, sizeof(srcip
));
628 plog("setting virtual IP source address to %s", srcip
);
631 /* setting client subnet as srcip/32 */
632 addrtosubnet(&a
, &c
->spd
.this.client
);
633 c
->spd
.this.has_client
= TRUE
;
635 resp
|= LELEM(attr
.isaat_af_type
);
637 case INTERNAL_IP4_NETMASK
:
638 case INTERNAL_IP4_DNS
:
639 case INTERNAL_IP4_SUBNET
:
640 case INTERNAL_IP4_NBNS
:
641 resp
|= LELEM(attr
.isaat_af_type
);
644 plog("unsupported mode cfg attribute %s received."
645 , enum_show(&modecfg_attr_names
, (attr
.isaat_af_type
& ISAKMP_ATTR_RTYPE_MASK
)));
652 stat
= modecfg_resp(st
, resp
660 /* notification payload - not exactly the right choice, but okay */
661 md
->note
= CERTIFICATE_UNAVAILABLE
;
666 * we are done with this exchange, clear things so
667 * that we can start phase 2 properly
673 st
->st_modecfg
.vars_set
= TRUE
;
678 /* STATE_MODE_CFG_R1:
679 * HDR*, HASH, ATTR(SET=IP) --> HDR*, HASH, ATTR(ACK,OK)
682 modecfg_inR1(struct msg_digest
*md
)
684 struct state
*const st
= md
->st
;
685 pb_stream
*attrs
= &md
->chain
[ISAKMP_NEXT_ATTR
]->pbs
;
686 int set_modecfg_attrs
= LEMPTY
;
688 struct payload_digest
*p
;
690 plog("parsing ModeCfg reply");
692 st
->st_msgid
= md
->hdr
.isa_msgid
;
693 CHECK_QUICK_HASH(md
, mode_cfg_hash(hash_val
,hash_pbs
->roof
, md
->message_pbs
.roof
, st
)
694 , "MODECFG-HASH", "MODE R1");
697 /* process the MODECFG payloads therein */
698 for (p
= md
->chain
[ISAKMP_NEXT_ATTR
]; p
!= NULL
; p
= p
->next
)
700 struct isakmp_attribute attr
;
705 switch (p
->payload
.attribute
.isama_type
)
709 plog("Expecting MODE_CFG_ACK, got %x instead."
710 ,md
->chain
[ISAKMP_NEXT_ATTR
]->payload
.attribute
.isama_type
);
716 /* CHECK that ACK has been received. */
717 stat
= modecfg_parse_attributes(attrs
, &set_modecfg_attrs
);
722 case ISAKMP_CFG_REPLY
:
723 while (pbs_left(attrs
) > sizeof(struct isakmp_attribute
))
725 if (!in_struct(&attr
, &isakmp_modecfg_attribute_desc
730 if (attr
.isaat_af_type
& 0x8000)
737 plog("Attribute was too short: %d", len
);
744 switch (attr
.isaat_af_type
& ISAKMP_ATTR_RTYPE_MASK
)
746 case INTERNAL_IP4_ADDRESS
:
748 struct connection
*c
= st
->st_connection
;
750 u_int32_t
*ap
= (u_int32_t
*)(strattr
.cur
);
751 a
.u
.v4
.sin_family
= AF_INET
;
753 memcpy(&a
.u
.v4
.sin_addr
.s_addr
, ap
754 , sizeof(a
.u
.v4
.sin_addr
.s_addr
));
756 if (addrbytesptr(&c
->spd
.this.host_srcip
, NULL
) == 0
757 || isanyaddr(&c
->spd
.this.host_srcip
))
759 char srcip
[ADDRTOT_BUF
];
761 c
->spd
.this.host_srcip
= a
;
762 addrtot(&a
, 0, srcip
, sizeof(srcip
));
763 plog("setting virtual IP source address to %s", srcip
);
766 /* setting client subnet as srcip/32 */
767 addrtosubnet(&a
, &c
->spd
.this.client
);
768 setportof(0, &c
->spd
.this.client
.addr
);
769 c
->spd
.this.has_client
= TRUE
;
771 /* fall through to set attribute flage */
773 case INTERNAL_IP4_NETMASK
:
774 case INTERNAL_IP4_DNS
:
775 case INTERNAL_IP4_SUBNET
:
776 case INTERNAL_IP4_NBNS
:
777 set_modecfg_attrs
|= LELEM(attr
.isaat_af_type
);
780 plog("unsupported mode cfg attribute %s received."
781 , enum_show(&modecfg_attr_names
782 , (attr
.isaat_af_type
& ISAKMP_ATTR_RTYPE_MASK
)));
790 /* we are done with this exchange, clear things so that we can start phase 2 properly */
793 if (set_modecfg_attrs
)
795 st
->st_modecfg
.vars_set
= TRUE
;