1 /*        $NetBSD: isakmp_unity.c,v 1.12 2025/03/07 15:55:29 christos Exp $     */
2 
3 /* Id: isakmp_unity.c,v 1.10 2006/07/31 04:49:23 manubsd Exp */
4 
5 /*
6  * Copyright (C) 2004 Emmanuel Dreyfus
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
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.
17  * 3. Neither the name of the project nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include "config.h"
35 
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/socket.h>
39 #include <sys/queue.h>
40 
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <fcntl.h>
47 #include <string.h>
48 #include <errno.h>
49 #if TIME_WITH_SYS_TIME
50 # include <sys/time.h>
51 # include <time.h>
52 #else
53 # if HAVE_SYS_TIME_H
54 #  include <sys/time.h>
55 # else
56 #  include <time.h>
57 # endif
58 #endif
59 #include <netdb.h>
60 #ifdef HAVE_UNISTD_H
61 #include <unistd.h>
62 #endif
63 #include <ctype.h>
64 #include <resolv.h>
65 #ifdef HAVE_STRINGS_H
66 #include <strings.h>
67 #endif
68 
69 #include "var.h"
70 #include "misc.h"
71 #include "vmbuf.h"
72 #include "plog.h"
73 #include "sockmisc.h"
74 #include "schedule.h"
75 #include "debug.h"
76 
77 #include "isakmp_var.h"
78 #include "isakmp.h"
79 #include "handler.h"
80 #include "isakmp_xauth.h"
81 #include "isakmp_unity.h"
82 #include "isakmp_cfg.h"
83 #include "strnames.h"
84 
85 static vchar_t *isakmp_cfg_split(struct ph1handle *,
86     struct isakmp_data *, struct unity_netentry*,int);
87 
88 vchar_t *
isakmp_unity_req(struct ph1handle * iph1,struct isakmp_data * attr)89 isakmp_unity_req(struct ph1handle *iph1, struct isakmp_data *attr)
90 {
91           int type;
92           vchar_t *reply_attr = NULL;
93 
94           if ((iph1->mode_cfg->flags & ISAKMP_CFG_VENDORID_UNITY) == 0) {
95                     plog(LLV_ERROR, LOCATION, NULL,
96                         "Unity mode config request but the peer "
97                         "did not declare itself as  unity compliant\n");
98                     return NULL;
99           }
100 
101           type = ntohs(attr->type);
102 
103           /* Handle short attributes */
104           if ((type & ISAKMP_GEN_MASK) == ISAKMP_GEN_TV) {
105                     type &= ~ISAKMP_GEN_MASK;
106 
107                     plog(LLV_DEBUG, LOCATION, NULL,
108                          "Short attribute %s = %d\n",
109                          s_isakmp_cfg_type(type), ntohs(attr->lorv));
110 
111                     switch (type) {
112                     default:
113                               plog(LLV_DEBUG, LOCATION, NULL,
114                                    "Ignored short attribute %s\n",
115                                    s_isakmp_cfg_type(type));
116                               break;
117                     }
118 
119                     return reply_attr;
120           }
121 
122           switch(type) {
123           case UNITY_BANNER: {
124 #define MAXMOTD 65536
125                     char buf[MAXMOTD + 1];
126                     int fd;
127                     char *filename = &isakmp_cfg_config.motd[0];
128                     ssize_t len;
129 
130                     if ((fd = open(filename, O_RDONLY, 0)) == -1) {
131                               plog(LLV_ERROR, LOCATION, NULL,
132                                   "Cannot open \"%s\"\n", filename);
133                               return NULL;
134                     }
135 
136                     if ((len = read(fd, buf, MAXMOTD)) == -1) {
137                               plog(LLV_ERROR, LOCATION, NULL,
138                                   "Cannot read \"%s\"\n", filename);
139                               close(fd);
140                               return NULL;
141                     }
142                     close(fd);
143 
144                     buf[len] = '\0';
145                     reply_attr = isakmp_cfg_string(iph1, attr, buf);
146 
147                     break;
148           }
149 
150           case UNITY_PFS:
151                     reply_attr = isakmp_cfg_short(iph1, attr,
152                         isakmp_cfg_config.pfs_group);
153                     break;
154 
155           case UNITY_SAVE_PASSWD:
156                     reply_attr = isakmp_cfg_short(iph1, attr,
157                         isakmp_cfg_config.save_passwd);
158                     break;
159 
160           case UNITY_DDNS_HOSTNAME:
161                     reply_attr = isakmp_cfg_copy(iph1, attr);
162                     break;
163 
164           case UNITY_DEF_DOMAIN:
165                     reply_attr = isakmp_cfg_string(iph1,
166                         attr, isakmp_cfg_config.default_domain);
167                     break;
168 
169           case UNITY_SPLIT_INCLUDE:
170                     if(isakmp_cfg_config.splitnet_type == UNITY_SPLIT_INCLUDE)
171                               reply_attr = isakmp_cfg_split(iph1, attr,
172                               isakmp_cfg_config.splitnet_list,
173                               isakmp_cfg_config.splitnet_count);
174                     else
175                               return NULL;
176                     break;
177           case UNITY_LOCAL_LAN:
178                     if(isakmp_cfg_config.splitnet_type == UNITY_LOCAL_LAN)
179                               reply_attr = isakmp_cfg_split(iph1, attr,
180                               isakmp_cfg_config.splitnet_list,
181                               isakmp_cfg_config.splitnet_count);
182                     else
183                               return NULL;
184                     break;
185           case UNITY_SPLITDNS_NAME:
186                     reply_attr = isakmp_cfg_varlen(iph1, attr,
187                                         isakmp_cfg_config.splitdns_list,
188                                         isakmp_cfg_config.splitdns_len);
189                     break;
190           case UNITY_FW_TYPE:
191           case UNITY_NATT_PORT:
192           case UNITY_BACKUP_SERVERS:
193           default:
194                     plog(LLV_DEBUG, LOCATION, NULL,
195                          "Ignored attribute %s\n", s_isakmp_cfg_type(type));
196                     return NULL;
197           }
198 
199           return reply_attr;
200 }
201 
202 void
isakmp_unity_reply(struct ph1handle * iph1,struct isakmp_data * attr)203 isakmp_unity_reply(struct ph1handle *iph1, struct isakmp_data *attr)
204 {
205           int type = ntohs(attr->type);
206           int alen = ntohs(attr->lorv);
207 
208           struct unity_network *network = (struct unity_network *)(attr + 1);
209           int index1 = 0;
210           size_t count = 0;
211 
212           switch(type) {
213           case UNITY_SPLIT_INCLUDE:
214           {
215                     if (alen)
216                               count = alen / sizeof(struct unity_network);
217 
218                     for(;index1 < count; index1++)
219                               splitnet_list_add(
220                                         &iph1->mode_cfg->split_include,
221                                         &network[index1],
222                                         &iph1->mode_cfg->include_count);
223 
224                     iph1->mode_cfg->flags |= ISAKMP_CFG_GOT_SPLIT_INCLUDE;
225                     break;
226           }
227           case UNITY_LOCAL_LAN:
228           {
229                     if (alen)
230                               count = alen / sizeof(struct unity_network);
231 
232                     for(;index1 < count; index1++)
233                               splitnet_list_add(
234                                         &iph1->mode_cfg->split_local,
235                                         &network[index1],
236                                         &iph1->mode_cfg->local_count);
237 
238                     iph1->mode_cfg->flags |= ISAKMP_CFG_GOT_SPLIT_LOCAL;
239                     break;
240           }
241           case UNITY_SPLITDNS_NAME:
242           case UNITY_BANNER:
243           case UNITY_SAVE_PASSWD:
244           case UNITY_NATT_PORT:
245           case UNITY_PFS:
246           case UNITY_FW_TYPE:
247           case UNITY_BACKUP_SERVERS:
248           case UNITY_DDNS_HOSTNAME:
249           default:
250                     plog(LLV_WARNING, LOCATION, NULL,
251                          "Ignored attribute %s\n",
252                          s_isakmp_cfg_type(type));
253                     break;
254           }
255           return;
256 }
257 
258 static vchar_t *
isakmp_cfg_split(struct ph1handle * iph1,struct isakmp_data * attr,struct unity_netentry * netentry,int count)259 isakmp_cfg_split(struct ph1handle *iph1, struct isakmp_data *attr,
260     struct unity_netentry *netentry, int count)
261 {
262           vchar_t *buffer;
263           struct isakmp_data *new;
264           struct unity_network * network;
265           size_t len;
266           int index1 = 0;
267 
268           char tmp1[40];
269           char tmp2[40];
270 
271           len = sizeof(struct unity_network) * count;
272           if ((buffer = vmalloc(sizeof(*attr) + len)) == NULL) {
273                     plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n");
274                     return NULL;
275           }
276 
277           new = (struct isakmp_data *)buffer->v;
278           new->type = attr->type;
279           new->lorv = htons(len);
280 
281           network = (struct unity_network *)(new + 1);
282           for (; index1 < count; index1++) {
283 
284                     memcpy(&network[index1],
285                               &netentry->network,
286                               sizeof(struct unity_network));
287 
288                     inet_ntop(AF_INET, &netentry->network.addr4, tmp1, 40);
289                     inet_ntop(AF_INET, &netentry->network.mask4, tmp2, 40);
290                     plog(LLV_DEBUG, LOCATION, NULL, "splitnet: %s/%s\n", tmp1, tmp2);
291 
292                     netentry = netentry->next;
293           }
294 
295           return buffer;
296 }
297 
splitnet_list_add(struct unity_netentry ** list,struct unity_network * network,int * count)298 int  splitnet_list_add(struct unity_netentry ** list,
299     struct unity_network * network, int *count)
300 {
301           struct unity_netentry * nentry;
302 
303           /*
304            * search for network in current list
305            * to avoid adding duplicates
306            */
307           for (nentry = *list; nentry != NULL; nentry = nentry->next)
308                     if (memcmp(&nentry->network, network,
309                                  sizeof(struct unity_network)) == 0)
310                               return 0; /* it's a dupe */
311 
312           /*
313            * allocate new netentry and copy
314            * new splitnet network data
315            */
316           nentry = (struct unity_netentry *)
317                     racoon_malloc(sizeof(struct unity_netentry));
318           if (nentry == NULL)
319                     return -1;
320 
321           memcpy(&nentry->network,network,
322                     sizeof(struct unity_network));
323           nentry->next = NULL;
324 
325           /*
326            * locate the last netentry in our
327            * splitnet list and add our entry
328            */
329           if (*list == NULL)
330                     *list = nentry;
331           else {
332                     struct unity_netentry * tmpentry = *list;
333                     while (tmpentry->next != NULL)
334                               tmpentry = tmpentry->next;
335                     tmpentry->next = nentry;
336           }
337 
338           (*count)++;
339 
340           return 0;
341 }
342 
343 void
splitnet_list_free(struct unity_netentry * list,int * count)344 splitnet_list_free(struct unity_netentry * list, int *count)
345 {
346           struct unity_netentry * netentry = list;
347           struct unity_netentry * delentry;
348 
349           *count = 0;
350 
351           while (netentry != NULL) {
352                     delentry = netentry;
353                     netentry = netentry->next;
354                     racoon_free(delentry);
355           }
356 }
357 
358 char *
splitnet_list_2str(struct unity_netentry * list,enum splinet_ipaddr splitnet_ipaddr)359 splitnet_list_2str(struct unity_netentry * list,
360     enum splinet_ipaddr splitnet_ipaddr)
361 {
362           struct unity_netentry * netentry;
363           char tmp1[40];
364           char tmp2[40];
365           char * str;
366           size_t len;
367 
368           /* determine string length */
369           len = 0;
370           netentry = list;
371           while (netentry != NULL) {
372 
373                     inet_ntop(AF_INET, &netentry->network.addr4, tmp1, 40);
374                     inet_ntop(AF_INET, &netentry->network.mask4, tmp2, 40);
375                     len += strlen(tmp1);
376                     len += strlen(tmp2);
377                     len += 2;
378 
379                     netentry = netentry->next;
380           }
381 
382           /* allocate network list string; we need the extra byte temporarily
383            * as sprintf() will write trailing 0-byte after the space. */
384           str = racoon_malloc(len + 1);
385           if (str == NULL)
386                     return NULL;
387 
388           /* create network list string */
389           len = 0;
390           netentry = list;
391           while (netentry != NULL) {
392 
393                     inet_ntop(AF_INET, &netentry->network.addr4, tmp1, 40);
394                     if (splitnet_ipaddr == CIDR) {
395                               uint32_t tmp3;
396                               int cidrmask;
397 
398                               tmp3 = ntohl(netentry->network.mask4.s_addr);
399                               cidrmask = 33 - ffs(tmp3);
400                               if (cidrmask == 33) cidrmask = 0;
401 
402                               len += sprintf(str+len, "%s/%d ", tmp1, cidrmask);
403                     } else {
404                               inet_ntop(AF_INET, &netentry->network.mask4, tmp2, 40);
405                               len += sprintf(str+len, "%s/%s ", tmp1, tmp2);
406                     }
407 
408                     netentry = netentry->next;
409           }
410 
411           /* trim the string to not have trailing spaces */
412           str[len-1]=0;
413 
414           return str;
415 }
416