1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <syslog.h>
29 #include <libintl.h>
30 #include <strings.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <synch.h>
34 #include <stropts.h>
35 #include <errno.h>
36 #include <pthread.h>
37
38 #include <inet/ip.h>
39 #include <net/if.h>
40 #include <netinet/in.h>
41 #include <netdb.h>
42 #include <net/route.h>
43 #include <arpa/inet.h>
44
45 #include <sys/socket.h>
46 #include <sys/sockio.h>
47 #include <sys/systeminfo.h>
48
49 #include <smbsrv/libsmb.h>
50
51 #define SMB_NIC_DB_NAME "/var/smb/smbhosts.db"
52 #define SMB_NIC_DB_TIMEOUT 3000 /* in millisecond */
53 #define SMB_NIC_DB_VERMAJOR 1
54 #define SMB_NIC_DB_VERMINOR 0
55 #define SMB_NIC_DB_MAGIC 0x484F5354 /* HOST */
56
57 #define SMB_NIC_DB_ORD 1 /* open read-only */
58 #define SMB_NIC_DB_ORW 2 /* open read/write */
59
60 #define SMB_NIC_DB_SQL \
61 "CREATE TABLE db_info (" \
62 " ver_major INTEGER," \
63 " ver_minor INTEGER," \
64 " magic INTEGER" \
65 ");" \
66 "" \
67 "CREATE TABLE hosts (" \
68 " hostname TEXT PRIMARY KEY," \
69 " comment TEXT," \
70 " ifnames TEXT" \
71 ");"
72
73 #define SMB_NIC_HTBL_NCOL 3
74 #define SMB_NIC_HTBL_HOST 0
75 #define SMB_NIC_HTBL_CMNT 1
76 #define SMB_NIC_HTBL_IFS 2
77
78 #define NULL_MSGCHK(msg) ((msg) ? (msg) : "NULL")
79
80 #define SMB_NIC_MAXIFS 256
81 #define SMB_NIC_MAXEXCLLIST_LEN 512
82
83 typedef struct smb_hostifs {
84 list_node_t if_lnd;
85 char if_host[MAXHOSTNAMELEN];
86 char if_cmnt[SMB_PI_MAX_COMMENT];
87 char *if_names[SMB_NIC_MAXIFS];
88 int if_num;
89 } smb_hostifs_t;
90
91 typedef struct smb_hosts {
92 list_t h_list;
93 int h_num;
94 int h_ifnum;
95 } smb_hosts_t;
96
97 typedef struct {
98 smb_nic_t *nl_nics;
99 int nl_cnt; /* number of smb_nic_t structures */
100 int nl_hcnt; /* number of host names */
101 long nl_seqnum; /* a random sequence number */
102 rwlock_t nl_rwl;
103 } smb_niclist_t;
104
105 static int smb_nic_list_create(void);
106 static void smb_nic_list_destroy(void);
107
108 static int smb_nic_hlist_create(smb_hosts_t *);
109 static void smb_nic_hlist_destroy(smb_hosts_t *);
110 static int smb_nic_hlist_dbget(smb_hosts_t *);
111 static int smb_nic_hlist_sysget(smb_hosts_t *);
112
113 static void smb_nic_iflist_destroy(smb_hostifs_t *);
114 static smb_hostifs_t *smb_nic_iflist_decode(const char **, int *);
115
116 static int smb_nic_dbcreate(void);
117 static sqlite *smb_nic_dbopen(int);
118 static void smb_nic_dbclose(sqlite *);
119 static boolean_t smb_nic_dbexists(void);
120 static boolean_t smb_nic_dbvalidate(void);
121 static int smb_nic_dbaddhost(const char *, const char *, char *);
122 static int smb_nic_dbdelhost(const char *);
123 static int smb_nic_dbsetinfo(sqlite *);
124
125 static int smb_nic_getinfo(char *, smb_nic_t *, int);
126 static boolean_t smb_nic_nbt_exclude(const smb_nic_t *, const char **, int);
127 static int smb_nic_nbt_get_exclude_list(char *, char **, int);
128
129 static void smb_close_sockets(int, int);
130 static boolean_t smb_duplicate_nic(smb_hostifs_t *iflist, struct lifreq *lifrp);
131
132 /* This is the list we will monitor */
133 static smb_niclist_t smb_niclist;
134
135 /*
136 * smb_nic_init
137 *
138 * Initializes the interface list.
139 */
140 int
smb_nic_init(void)141 smb_nic_init(void)
142 {
143 int rc;
144
145 (void) rw_wrlock(&smb_niclist.nl_rwl);
146 smb_nic_list_destroy();
147 rc = smb_nic_list_create();
148 (void) rw_unlock(&smb_niclist.nl_rwl);
149
150 return (rc);
151 }
152
153 /*
154 * smb_nic_fini
155 *
156 * Destroys the interface list.
157 */
158 void
smb_nic_fini(void)159 smb_nic_fini(void)
160 {
161 (void) rw_wrlock(&smb_niclist.nl_rwl);
162 smb_nic_list_destroy();
163 (void) rw_unlock(&smb_niclist.nl_rwl);
164 }
165
166 /*
167 * smb_nic_getnum
168 *
169 * Gets the number of interfaces for the specified host.
170 * if host is NULL then total number of interfaces
171 * is returned. It's assumed that given name is a NetBIOS
172 * encoded name.
173 */
174 int
smb_nic_getnum(char * nb_hostname)175 smb_nic_getnum(char *nb_hostname)
176 {
177 int n = 0, i;
178
179 (void) rw_rdlock(&smb_niclist.nl_rwl);
180
181 if (nb_hostname != NULL) {
182 for (i = 0; i < smb_niclist.nl_cnt; i++) {
183 /* ignore the suffix */
184 if (strncasecmp(smb_niclist.nl_nics[i].nic_nbname,
185 nb_hostname, NETBIOS_NAME_SZ - 1) == 0)
186 n++;
187 }
188 } else {
189 n = smb_niclist.nl_cnt;
190 }
191
192 (void) rw_unlock(&smb_niclist.nl_rwl);
193
194 return (n);
195 }
196
197 /*
198 * smb_nic_getfirst
199 *
200 * Returns the first NIC in the interface list and
201 * initializes the given iterator. To get the rest of
202 * NICs smb_nic_getnext() must be called.
203 *
204 * Returns SMB_NIC_SUCCESS upon success or the following:
205 * SMB_NIC_NOT_FOUND - there's no interface available
206 * SMB_NIC_INVALID_ARG - 'ni' is NULL
207 */
208 int
smb_nic_getfirst(smb_niciter_t * ni)209 smb_nic_getfirst(smb_niciter_t *ni)
210 {
211 int rc = SMB_NIC_SUCCESS;
212
213 if (ni == NULL)
214 return (SMB_NIC_INVALID_ARG);
215
216 (void) rw_rdlock(&smb_niclist.nl_rwl);
217
218 if (smb_niclist.nl_cnt > 0) {
219 ni->ni_nic = smb_niclist.nl_nics[0];
220 ni->ni_cookie = 1;
221 ni->ni_seqnum = smb_niclist.nl_seqnum;
222 } else {
223 rc = SMB_NIC_NOT_FOUND;
224 }
225
226 (void) rw_unlock(&smb_niclist.nl_rwl);
227
228 return (rc);
229 }
230
231 /*
232 * smb_nic_getnext
233 *
234 * Returns the next NIC information based on the passed
235 * iterator (ni). The iterator must have previously been
236 * initialized by calling smb_nic_getfirst().
237 *
238 * Returns SMB_NIC_SUCCESS upon successfully finding the specified NIC
239 * or the following:
240 * SMB_NIC_INVALID_ARG - the specified iterator is invalid
241 * SMB_NIC_NO_MORE - reaches the end of the NIC list
242 * SMB_NIC_CHANGED - sequence number in the iterator is different from
243 * the sequence number in the NIC list which means
244 * the list has been changed between getfirst/getnext
245 * calls.
246 */
247 int
smb_nic_getnext(smb_niciter_t * ni)248 smb_nic_getnext(smb_niciter_t *ni)
249 {
250 int rc = SMB_NIC_SUCCESS;
251
252 if ((ni == NULL) || (ni->ni_cookie < 1))
253 return (SMB_NIC_INVALID_ARG);
254
255 (void) rw_rdlock(&smb_niclist.nl_rwl);
256
257 if ((smb_niclist.nl_cnt > ni->ni_cookie) &&
258 (smb_niclist.nl_seqnum == ni->ni_seqnum)) {
259 ni->ni_nic = smb_niclist.nl_nics[ni->ni_cookie];
260 ni->ni_cookie++;
261 } else {
262 if (smb_niclist.nl_seqnum != ni->ni_seqnum)
263 rc = SMB_NIC_CHANGED;
264 else
265 rc = SMB_NIC_NO_MORE;
266 }
267
268 (void) rw_unlock(&smb_niclist.nl_rwl);
269
270 return (rc);
271 }
272
273 boolean_t
smb_nic_is_local(smb_inaddr_t * ipaddr)274 smb_nic_is_local(smb_inaddr_t *ipaddr)
275 {
276 smb_nic_t *cfg;
277 int i;
278
279 (void) rw_rdlock(&smb_niclist.nl_rwl);
280
281 for (i = 0; i < smb_niclist.nl_cnt; i++) {
282 cfg = &smb_niclist.nl_nics[i];
283 if (smb_inet_equal(ipaddr, &cfg->nic_ip)) {
284 (void) rw_unlock(&smb_niclist.nl_rwl);
285 return (B_TRUE);
286 }
287 }
288 (void) rw_unlock(&smb_niclist.nl_rwl);
289 return (B_FALSE);
290 }
291
292 boolean_t
smb_nic_is_same_subnet(smb_inaddr_t * ipaddr)293 smb_nic_is_same_subnet(smb_inaddr_t *ipaddr)
294 {
295 smb_nic_t *cfg;
296 int i;
297
298 (void) rw_rdlock(&smb_niclist.nl_rwl);
299
300 for (i = 0; i < smb_niclist.nl_cnt; i++) {
301 cfg = &smb_niclist.nl_nics[i];
302 if (smb_inet_same_subnet(ipaddr, &cfg->nic_ip, cfg->nic_mask)) {
303 (void) rw_unlock(&smb_niclist.nl_rwl);
304 return (B_TRUE);
305 }
306 }
307 (void) rw_unlock(&smb_niclist.nl_rwl);
308 return (B_FALSE);
309 }
310
311 /*
312 * smb_nic_addhost
313 *
314 * Adds an association between the given host and the specified interface
315 * list (if_names). This function can be called as many times as needed,
316 * the associations will be stored in /var/smb/smbhosts.db, which is sqlite
317 * database. If this file exists and it's not empty NIC list is generated
318 * based on the information stored in this file.
319 *
320 * host: actual system's name (not Netbios name)
321 * cmnt: an optional description for the CIFS server running on
322 * the specified host. Can be NULL.
323 * if_num: number of interface names in if_names arg
324 * if_names: array of interface names in string format
325 *
326 * Returns SMB_NIC_SUCCESS upon success, a nonzero value otherwise.
327 */
328 int
smb_nic_addhost(const char * host,const char * cmnt,int if_num,const char ** if_names)329 smb_nic_addhost(const char *host, const char *cmnt,
330 int if_num, const char **if_names)
331 {
332 char *if_list;
333 char *ifname;
334 int buflen = 0;
335 int rc, i;
336
337 if ((host == NULL) || (if_num <= 0) || (if_names == NULL))
338 return (SMB_NIC_INVALID_ARG);
339
340 if (!smb_nic_dbexists() || !smb_nic_dbvalidate()) {
341 if ((rc = smb_nic_dbcreate()) != SMB_NIC_SUCCESS)
342 return (rc);
343 }
344
345 for (i = 0; i < if_num; i++) {
346 ifname = (char *)if_names[i];
347 if ((ifname == NULL) || (*ifname == '\0'))
348 return (SMB_NIC_INVALID_ARG);
349 buflen += strlen(ifname) + 1;
350 }
351
352 if ((if_list = malloc(buflen)) == NULL)
353 return (SMB_NIC_NO_MEMORY);
354
355 ifname = if_list;
356 for (i = 0; i < if_num - 1; i++)
357 ifname += snprintf(ifname, buflen, "%s,", if_names[i]);
358
359 (void) snprintf(ifname, buflen, "%s", if_names[i]);
360
361 rc = smb_nic_dbaddhost(host, cmnt, if_list);
362 free(if_list);
363
364 return (rc);
365 }
366
367 /*
368 * smb_nic_delhost
369 *
370 * Removes the stored interface association for the specified host
371 */
372 int
smb_nic_delhost(const char * host)373 smb_nic_delhost(const char *host)
374 {
375 if ((host == NULL) || (*host == '\0'))
376 return (SMB_NIC_INVALID_ARG);
377
378 if (!smb_nic_dbexists())
379 return (SMB_NIC_SUCCESS);
380
381 if (!smb_nic_dbvalidate()) {
382 (void) unlink(SMB_NIC_DB_NAME);
383 return (SMB_NIC_SUCCESS);
384 }
385
386 return (smb_nic_dbdelhost(host));
387 }
388
389 /*
390 * smb_nic_list_create
391 *
392 * Creates a NIC list either based on /var/smb/smbhosts.db or
393 * by getting the information from OS.
394 *
395 * Note that the caller of this function should grab the
396 * list lock.
397 */
398 static int
smb_nic_list_create(void)399 smb_nic_list_create(void)
400 {
401 smb_hosts_t hlist;
402 smb_hostifs_t *iflist;
403 smb_nic_t *nc;
404 char *ifname;
405 char excludestr[SMB_NIC_MAXEXCLLIST_LEN];
406 char *exclude[SMB_PI_MAX_NETWORKS];
407 int nexclude = 0;
408 int i, rc;
409
410 if ((rc = smb_nic_hlist_create(&hlist)) != SMB_NIC_SUCCESS)
411 return (rc);
412
413 smb_niclist.nl_cnt = 0;
414 smb_niclist.nl_seqnum = random();
415 smb_niclist.nl_hcnt = hlist.h_num;
416
417 smb_niclist.nl_nics = calloc(hlist.h_ifnum, sizeof (smb_nic_t));
418 if (smb_niclist.nl_nics == NULL) {
419 smb_nic_hlist_destroy(&hlist);
420 return (SMB_NIC_NO_MEMORY);
421 }
422
423 *excludestr = '\0';
424 (void) smb_config_getstr(SMB_CI_WINS_EXCL,
425 excludestr, sizeof (excludestr));
426
427 nexclude = smb_nic_nbt_get_exclude_list(excludestr,
428 exclude, SMB_PI_MAX_NETWORKS);
429
430 nc = smb_niclist.nl_nics;
431 iflist = list_head(&hlist.h_list);
432
433 do {
434 for (i = 0; i < iflist->if_num; i++) {
435 ifname = iflist->if_names[i];
436 if (smb_nic_getinfo(ifname, nc, AF_INET) !=
437 SMB_NIC_SUCCESS) {
438 if (smb_nic_getinfo(ifname, nc,
439 AF_INET6) != SMB_NIC_SUCCESS) {
440 continue;
441 }
442 }
443
444 (void) strlcpy(nc->nic_host, iflist->if_host,
445 sizeof (nc->nic_host));
446 (void) strlcpy(nc->nic_cmnt, iflist->if_cmnt,
447 sizeof (nc->nic_cmnt));
448
449 smb_tonetbiosname(nc->nic_host, nc->nic_nbname, 0x00);
450
451 if (strchr(ifname, ':'))
452 nc->nic_smbflags |= SMB_NICF_ALIAS;
453
454 if (smb_nic_nbt_exclude(nc,
455 (const char **)exclude, nexclude))
456 nc->nic_smbflags |= SMB_NICF_NBEXCL;
457
458 smb_niclist.nl_cnt++;
459 nc++;
460 }
461 } while ((iflist = list_next(&hlist.h_list, iflist)) != NULL);
462
463 smb_nic_hlist_destroy(&hlist);
464
465 return (SMB_NIC_SUCCESS);
466 }
467
468 static void
smb_nic_list_destroy(void)469 smb_nic_list_destroy(void)
470 {
471 free(smb_niclist.nl_nics);
472 smb_niclist.nl_nics = NULL;
473 smb_niclist.nl_cnt = 0;
474 }
475
476 static int
smb_nic_getinfo(char * interface,smb_nic_t * nc,int family)477 smb_nic_getinfo(char *interface, smb_nic_t *nc, int family)
478 {
479 struct lifreq lifrr;
480 int s;
481 boolean_t isv6;
482 struct sockaddr_in6 *sin6;
483 struct sockaddr_in *sin;
484
485 if ((s = socket(family, SOCK_DGRAM, IPPROTO_IP)) < 0) {
486 return (SMB_NIC_SOCK);
487 }
488
489 (void) strlcpy(lifrr.lifr_name, interface, sizeof (lifrr.lifr_name));
490 if (ioctl(s, SIOCGLIFADDR, &lifrr) < 0) {
491 (void) close(s);
492 return (SMB_NIC_IOCTL);
493 }
494 isv6 = (lifrr.lifr_addr.ss_family == AF_INET6);
495 if (isv6) {
496 sin6 = (struct sockaddr_in6 *)(&lifrr.lifr_addr);
497 nc->nic_ip.a_ipv6 = sin6->sin6_addr;
498 nc->nic_ip.a_family = AF_INET6;
499 } else {
500 sin = (struct sockaddr_in *)(&lifrr.lifr_addr);
501 nc->nic_ip.a_ipv4 = (in_addr_t)(sin->sin_addr.s_addr);
502 nc->nic_ip.a_family = AF_INET;
503 }
504 if (smb_inet_iszero(&nc->nic_ip)) {
505 (void) close(s);
506 return (SMB_NIC_BAD_DATA);
507 }
508 /* there is no broadcast or netmask for v6 */
509 if (!isv6) {
510 if (ioctl(s, SIOCGLIFBRDADDR, &lifrr) < 0) {
511 (void) close(s);
512 return (SMB_NIC_IOCTL);
513 }
514 sin = (struct sockaddr_in *)&lifrr.lifr_broadaddr;
515 nc->nic_bcast = (uint32_t)sin->sin_addr.s_addr;
516
517 if (ioctl(s, SIOCGLIFNETMASK, &lifrr) < 0) {
518 (void) close(s);
519 return (SMB_NIC_IOCTL);
520 }
521 sin = (struct sockaddr_in *)&lifrr.lifr_addr;
522 nc->nic_mask = (uint32_t)sin->sin_addr.s_addr;
523 }
524 if (ioctl(s, SIOCGLIFFLAGS, &lifrr) < 0) {
525 (void) close(s);
526 return (SMB_NIC_IOCTL);
527 }
528 nc->nic_sysflags = lifrr.lifr_flags;
529
530 (void) strlcpy(nc->nic_ifname, interface, sizeof (nc->nic_ifname));
531
532 (void) close(s);
533 return (SMB_NIC_SUCCESS);
534 }
535
536 /*
537 * smb_nic_hlist_create
538 *
539 * Creates a list of hosts and their associated interfaces.
540 * If host database exists the information is retrieved from
541 * the database, otherwise it's retrieved from OS.
542 *
543 * The allocated memories for the returned list should be freed
544 * by calling smb_nic_hlist_destroy()
545 */
546 static int
smb_nic_hlist_create(smb_hosts_t * hlist)547 smb_nic_hlist_create(smb_hosts_t *hlist)
548 {
549 int rc;
550
551 list_create(&hlist->h_list, sizeof (smb_hostifs_t),
552 offsetof(smb_hostifs_t, if_lnd));
553 hlist->h_num = 0;
554 hlist->h_ifnum = 0;
555
556 if (smb_nic_dbexists() && smb_nic_dbvalidate()) {
557 rc = smb_nic_hlist_dbget(hlist);
558 errno = EBADF;
559 } else {
560 rc = smb_nic_hlist_sysget(hlist);
561 }
562
563 if (rc != SMB_NIC_SUCCESS)
564 smb_nic_hlist_destroy(hlist);
565
566 return (rc);
567 }
568
569 static void
smb_nic_hlist_destroy(smb_hosts_t * hlist)570 smb_nic_hlist_destroy(smb_hosts_t *hlist)
571 {
572 smb_hostifs_t *iflist;
573
574 if (hlist == NULL)
575 return;
576
577 while ((iflist = list_head(&hlist->h_list)) != NULL) {
578 list_remove(&hlist->h_list, iflist);
579 smb_nic_iflist_destroy(iflist);
580 }
581
582 list_destroy(&hlist->h_list);
583 }
584
585 static void
smb_close_sockets(int s4,int s6)586 smb_close_sockets(int s4, int s6)
587 {
588 if (s4)
589 (void) close(s4);
590 if (s6)
591 (void) close(s6);
592 }
593
594 /*
595 * smb_nic_hlist_sysget
596 *
597 * Get the list of currently plumbed and up interface names. The loopback (lo0)
598 * port is ignored
599 */
600 static int
smb_nic_hlist_sysget(smb_hosts_t * hlist)601 smb_nic_hlist_sysget(smb_hosts_t *hlist)
602 {
603 smb_hostifs_t *iflist;
604 struct lifconf lifc;
605 struct lifreq lifrl;
606 struct lifreq *lifrp;
607 char *ifname;
608 int ifnum;
609 int i;
610 int s4, s6;
611 struct lifnum lifn;
612
613 iflist = malloc(sizeof (smb_hostifs_t));
614 if (iflist == NULL)
615 return (SMB_NIC_NO_MEMORY);
616
617 bzero(iflist, sizeof (smb_hostifs_t));
618
619 if (smb_gethostname(iflist->if_host, sizeof (iflist->if_host),
620 SMB_CASE_PRESERVE) < 0) {
621 free(iflist);
622 return (SMB_NIC_NO_HOST);
623 }
624
625 (void) smb_config_getstr(SMB_CI_SYS_CMNT, iflist->if_cmnt,
626 sizeof (iflist->if_cmnt));
627
628 if ((s4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
629 free(iflist);
630 return (SMB_NIC_SOCK);
631 }
632 s6 = socket(AF_INET6, SOCK_DGRAM, 0);
633
634 lifn.lifn_family = AF_UNSPEC;
635 lifn.lifn_flags = 0;
636 if (ioctl(s4, SIOCGLIFNUM, (char *)&lifn) < 0) {
637 smb_close_sockets(s4, s6);
638 free(iflist);
639 syslog(LOG_ERR, "hlist_sysget: SIOCGLIFNUM errno=%d", errno);
640 return (SMB_NIC_IOCTL);
641 }
642
643 lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq);
644 lifc.lifc_buf = malloc(lifc.lifc_len);
645 if (lifc.lifc_buf == NULL) {
646 smb_close_sockets(s4, s6);
647 free(iflist);
648 return (SMB_NIC_NO_MEMORY);
649 }
650 bzero(lifc.lifc_buf, lifc.lifc_len);
651 lifc.lifc_family = AF_UNSPEC;
652 lifc.lifc_flags = 0;
653
654 if (ioctl(s4, SIOCGLIFCONF, (char *)&lifc) < 0) {
655 smb_close_sockets(s4, s6);
656 free(iflist);
657 free(lifc.lifc_buf);
658 return (SMB_NIC_IOCTL);
659 }
660
661 lifrp = lifc.lifc_req;
662 ifnum = lifc.lifc_len / sizeof (struct lifreq);
663 hlist->h_num = 0;
664 for (i = 0; i < ifnum; i++, lifrp++) {
665
666 if ((iflist->if_num > 0) && smb_duplicate_nic(iflist, lifrp))
667 continue;
668 /*
669 * Get the flags so that we can skip the loopback interface
670 */
671 (void) memset(&lifrl, 0, sizeof (lifrl));
672 (void) strlcpy(lifrl.lifr_name, lifrp->lifr_name,
673 sizeof (lifrl.lifr_name));
674
675 if (ioctl(s4, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0) {
676 if ((s6 < 0) ||
677 (ioctl(s6, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)) {
678 smb_close_sockets(s4, s6);
679 free(lifc.lifc_buf);
680 smb_nic_iflist_destroy(iflist);
681 return (SMB_NIC_IOCTL);
682 }
683 }
684 if (lifrl.lifr_flags & IFF_LOOPBACK) {
685 continue;
686 }
687
688 if ((lifrl.lifr_flags & IFF_UP) == 0) {
689 continue;
690 }
691 ifname = strdup(lifrp->lifr_name);
692 if (ifname == NULL) {
693 smb_close_sockets(s4, s6);
694 free(lifc.lifc_buf);
695 smb_nic_iflist_destroy(iflist);
696 return (SMB_NIC_NO_MEMORY);
697 }
698 iflist->if_names[iflist->if_num++] = ifname;
699 }
700 hlist->h_ifnum = iflist->if_num;
701 hlist->h_num = 1;
702 smb_close_sockets(s4, s6);
703 free(lifc.lifc_buf);
704 list_insert_tail(&hlist->h_list, iflist);
705
706 return (SMB_NIC_SUCCESS);
707 }
708
709 static boolean_t
smb_duplicate_nic(smb_hostifs_t * iflist,struct lifreq * lifrp)710 smb_duplicate_nic(smb_hostifs_t *iflist, struct lifreq *lifrp)
711 {
712 int j;
713 /*
714 * throw out duplicate names
715 */
716 for (j = 0; j < iflist->if_num; j++) {
717 if (strcmp(iflist->if_names[j],
718 lifrp->lifr_name) == 0)
719 return (B_TRUE);
720 }
721 return (B_FALSE);
722 }
723
724 static int
smb_nic_hlist_dbget(smb_hosts_t * hlist)725 smb_nic_hlist_dbget(smb_hosts_t *hlist)
726 {
727 smb_hostifs_t *iflist;
728 sqlite *db;
729 sqlite_vm *vm;
730 int err = SMB_NIC_SUCCESS;
731 const char **values;
732 char *sql;
733 char *errmsg = NULL;
734 int ncol, rc;
735
736 sql = sqlite_mprintf("SELECT * FROM hosts");
737 if (sql == NULL)
738 return (SMB_NIC_NO_MEMORY);
739
740 db = smb_nic_dbopen(SMB_NIC_DB_ORD);
741 if (db == NULL) {
742 sqlite_freemem(sql);
743 return (SMB_NIC_DBOPEN_FAILED);
744 }
745
746 rc = sqlite_compile(db, sql, NULL, &vm, &errmsg);
747 sqlite_freemem(sql);
748
749 if (rc != SQLITE_OK) {
750 smb_nic_dbclose(db);
751 syslog(LOG_ERR, "Failed to query hosts info from host " \
752 "database. Unable to create virtual machine (%s).",
753 NULL_MSGCHK(errmsg));
754 return (SMB_NIC_DB_ERROR);
755 }
756
757 do {
758 rc = sqlite_step(vm, &ncol, &values, NULL);
759 if (rc == SQLITE_ROW) {
760 if (ncol != SMB_NIC_HTBL_NCOL) {
761 err = SMB_NIC_DB_ERROR;
762 break;
763 }
764
765 if ((iflist = smb_nic_iflist_decode(values, &err)) ==
766 NULL) {
767 break;
768 }
769
770 list_insert_tail(&hlist->h_list, iflist);
771 hlist->h_num++;
772 hlist->h_ifnum += iflist->if_num;
773 }
774 } while (rc == SQLITE_ROW);
775
776 if (rc != SQLITE_DONE && err == SMB_NIC_SUCCESS) {
777 /* set this error if no previous error */
778 err = SMB_LGRP_DBEXEC_FAILED;
779 }
780
781 rc = sqlite_finalize(vm, &errmsg);
782 if (rc != SQLITE_OK) {
783 syslog(LOG_ERR, "Failed to query hosts info from host " \
784 "database. Unable to destroy virtual machine (%s).",
785 NULL_MSGCHK(errmsg));
786 if (err == SMB_NIC_SUCCESS) {
787 /* set this error if no previous error */
788 err = SMB_NIC_DB_ERROR;
789 }
790 }
791
792 smb_nic_dbclose(db);
793
794 return (err);
795 }
796
797 static smb_hostifs_t *
smb_nic_iflist_decode(const char ** values,int * err)798 smb_nic_iflist_decode(const char **values, int *err)
799 {
800 smb_hostifs_t *iflist;
801 char *host;
802 char *cmnt;
803 char *ifnames;
804 char *lasts;
805 char *ifname;
806 int if_num = 0;
807
808 host = (char *)values[SMB_NIC_HTBL_HOST];
809 cmnt = (char *)values[SMB_NIC_HTBL_CMNT];
810 ifnames = (char *)values[SMB_NIC_HTBL_IFS];
811
812 if ((host == NULL) || (ifnames == NULL)) {
813 *err = SMB_NIC_INVALID_ARG;
814 return (NULL);
815 }
816
817 iflist = malloc(sizeof (smb_hostifs_t));
818 if (iflist == NULL) {
819 *err = SMB_NIC_NO_MEMORY;
820 return (NULL);
821 }
822
823 bzero(iflist, sizeof (smb_hostifs_t));
824
825 (void) strlcpy(iflist->if_host, host, sizeof (iflist->if_host));
826 (void) strlcpy(iflist->if_cmnt, (cmnt) ? cmnt : "",
827 sizeof (iflist->if_cmnt));
828
829 if ((ifname = strtok_r(ifnames, ",", &lasts)) == NULL) {
830 *err = SMB_NIC_BAD_DATA;
831 return (NULL);
832 }
833
834 iflist->if_names[if_num++] = strdup(ifname);
835
836 while ((ifname = strtok_r(NULL, ",", &lasts)) != NULL)
837 iflist->if_names[if_num++] = strdup(ifname);
838
839 iflist->if_num = if_num;
840
841 for (if_num = 0; if_num < iflist->if_num; if_num++) {
842 if (iflist->if_names[if_num] == NULL) {
843 smb_nic_iflist_destroy(iflist);
844 *err = SMB_NIC_NO_MEMORY;
845 return (NULL);
846 }
847 }
848
849 *err = SMB_NIC_SUCCESS;
850 return (iflist);
851 }
852
853 /*
854 * smb_nic_iflist_destroy
855 *
856 * Frees allocated memory for the given IF names lists.
857 */
858 static void
smb_nic_iflist_destroy(smb_hostifs_t * iflist)859 smb_nic_iflist_destroy(smb_hostifs_t *iflist)
860 {
861 int i;
862
863 if (iflist == NULL)
864 return;
865
866 for (i = 0; i < iflist->if_num; i++)
867 free(iflist->if_names[i]);
868
869 free(iflist);
870 }
871
872 /*
873 * Functions to manage host/interface database
874 *
875 * Each entry in the hosts table associates a hostname with a
876 * list of interface names. The host/interface association could
877 * be added by calling smb_nic_addhost() and could be removed by
878 * calling smb_nic_delhost(). If the database exists and it contains
879 * valid information then the inteface list wouldn't be obtained
880 * from system using ioctl.
881 */
882
883 /*
884 * smb_nic_dbcreate
885 *
886 * Creates the host database based on the defined SQL statement.
887 * It also initializes db_info table.
888 */
889 static int
smb_nic_dbcreate(void)890 smb_nic_dbcreate(void)
891 {
892 sqlite *db = NULL;
893 char *errmsg = NULL;
894 int rc, err = SMB_NIC_SUCCESS;
895
896 (void) unlink(SMB_NIC_DB_NAME);
897
898 db = sqlite_open(SMB_NIC_DB_NAME, 0600, &errmsg);
899 if (db == NULL) {
900 syslog(LOG_ERR, "Failed to create host database (%s).",
901 NULL_MSGCHK(errmsg));
902 sqlite_freemem(errmsg);
903 return (SMB_NIC_DBOPEN_FAILED);
904 }
905
906 sqlite_busy_timeout(db, SMB_NIC_DB_TIMEOUT);
907 rc = sqlite_exec(db, "BEGIN TRANSACTION", NULL, NULL, &errmsg);
908 if (rc != SQLITE_OK) {
909 syslog(LOG_ERR, "Failed to create host database. Unable to " \
910 "begin database transaction (%s).", NULL_MSGCHK(errmsg));
911 sqlite_freemem(errmsg);
912 sqlite_close(db);
913 return (SMB_NIC_DBEXEC_FAILED);
914 }
915
916 if (sqlite_exec(db, SMB_NIC_DB_SQL, NULL, NULL, &errmsg) == SQLITE_OK) {
917 rc = sqlite_exec(db, "COMMIT TRANSACTION", NULL, NULL,
918 &errmsg);
919 if (rc == SQLITE_OK)
920 err = smb_nic_dbsetinfo(db);
921 if (err != SMB_NIC_SUCCESS)
922 rc = sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL,
923 &errmsg);
924 } else {
925 syslog(LOG_ERR, "Failed to create host database. Unable to " \
926 "initialize host database (%s).", NULL_MSGCHK(errmsg));
927 sqlite_freemem(errmsg);
928 rc = sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL,
929 &errmsg);
930 }
931
932 if (rc != SQLITE_OK) {
933 /* this is bad - database may be left in a locked state */
934 syslog(LOG_ERR, "Failed to create host database. Unable to " \
935 "close a transaction (%s).", NULL_MSGCHK(errmsg));
936 sqlite_freemem(errmsg);
937 err = SMB_NIC_DBINIT_FAILED;
938 }
939
940 (void) sqlite_close(db);
941 return (err);
942 }
943
944 /*
945 * smb_nic_dbopen
946 *
947 * Opens host database with the given mode.
948 */
949 static sqlite *
smb_nic_dbopen(int mode)950 smb_nic_dbopen(int mode)
951 {
952 sqlite *db;
953 char *errmsg = NULL;
954
955 db = sqlite_open(SMB_NIC_DB_NAME, mode, &errmsg);
956 if (db == NULL) {
957 syslog(LOG_ERR, "Failed to open host database: %s (%s).",
958 SMB_NIC_DB_NAME, NULL_MSGCHK(errmsg));
959 sqlite_freemem(errmsg);
960 }
961
962 return (db);
963 }
964
965 /*
966 * smb_nic_dbclose
967 *
968 * Closes the given database handle
969 */
970 static void
smb_nic_dbclose(sqlite * db)971 smb_nic_dbclose(sqlite *db)
972 {
973 if (db) {
974 sqlite_close(db);
975 }
976 }
977
978 static boolean_t
smb_nic_dbexists(void)979 smb_nic_dbexists(void)
980 {
981 return (access(SMB_NIC_DB_NAME, F_OK) == 0);
982 }
983
984 static boolean_t
smb_nic_dbvalidate(void)985 smb_nic_dbvalidate(void)
986 {
987 sqlite *db;
988 char *errmsg = NULL;
989 char *sql;
990 char **result;
991 int nrow, ncol;
992 boolean_t check = B_TRUE;
993 int rc;
994
995 sql = sqlite_mprintf("SELECT * FROM db_info");
996 if (sql == NULL)
997 return (B_FALSE);
998
999 db = smb_nic_dbopen(SMB_NIC_DB_ORW);
1000 if (db == NULL) {
1001 sqlite_freemem(sql);
1002 return (B_FALSE);
1003 }
1004
1005 rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
1006 sqlite_freemem(sql);
1007
1008 if (rc != SQLITE_OK) {
1009 syslog(LOG_ERR, "Failed to validate host database. Unable " \
1010 "to get database information (%s).", NULL_MSGCHK(errmsg));
1011 sqlite_freemem(errmsg);
1012 smb_nic_dbclose(db);
1013 return (B_FALSE);
1014 }
1015
1016 if (nrow != 1 || ncol != 3) {
1017 syslog(LOG_ERR, "Failed to validate host database: bad " \
1018 "db_info table.");
1019 sqlite_free_table(result);
1020 smb_nic_dbclose(db);
1021 return (B_FALSE);
1022 }
1023
1024 if ((atoi(result[3]) != SMB_NIC_DB_VERMAJOR) ||
1025 (atoi(result[4]) != SMB_NIC_DB_VERMINOR) ||
1026 (atoi(result[5]) != SMB_NIC_DB_MAGIC)) {
1027 syslog(LOG_ERR, "Failed to validate host database: bad " \
1028 "db_info content.");
1029 sqlite_free_table(result);
1030 smb_nic_dbclose(db);
1031 return (B_FALSE);
1032 }
1033 sqlite_free_table(result);
1034
1035 sql = sqlite_mprintf("SELECT hostname FROM hosts");
1036 if (sql == NULL) {
1037 smb_nic_dbclose(db);
1038 return (B_FALSE);
1039 }
1040
1041 rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
1042 sqlite_freemem(sql);
1043
1044 if (rc != SQLITE_OK) {
1045 syslog(LOG_ERR, "Failed to validate host database. Unable " \
1046 "to query for host (%s).", NULL_MSGCHK(errmsg));
1047 sqlite_freemem(errmsg);
1048 smb_nic_dbclose(db);
1049 return (B_FALSE);
1050 }
1051
1052 sqlite_free_table(result);
1053
1054 if (nrow == 0)
1055 /* No hosts in the database */
1056 check = B_FALSE;
1057
1058 smb_nic_dbclose(db);
1059 return (check);
1060 }
1061
1062 static int
smb_nic_dbaddhost(const char * host,const char * cmnt,char * if_list)1063 smb_nic_dbaddhost(const char *host, const char *cmnt, char *if_list)
1064 {
1065 sqlite *db;
1066 char *sql;
1067 char *errmsg;
1068 int rc, err = SMB_NIC_SUCCESS;
1069
1070 sql = sqlite_mprintf("REPLACE INTO hosts (hostname, comment, ifnames)"
1071 "VALUES ('%s', '%q', '%s')", host, (cmnt) ? cmnt : "", if_list);
1072 if (sql == NULL)
1073 return (SMB_NIC_NO_MEMORY);
1074
1075 db = smb_nic_dbopen(SMB_NIC_DB_ORW);
1076 if (db == NULL) {
1077 sqlite_freemem(sql);
1078 return (SMB_NIC_DBOPEN_FAILED);
1079 }
1080
1081 rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1082 sqlite_freemem(sql);
1083 smb_nic_dbclose(db);
1084
1085 if (rc != SQLITE_OK) {
1086 syslog(LOG_ERR, "Failed to add host %s to host database (%s).",
1087 host, NULL_MSGCHK(errmsg));
1088 sqlite_freemem(errmsg);
1089 err = SMB_NIC_INSERT_FAILED;
1090 }
1091
1092 return (err);
1093 }
1094
1095 static int
smb_nic_dbdelhost(const char * host)1096 smb_nic_dbdelhost(const char *host)
1097 {
1098 sqlite *db;
1099 char *sql;
1100 char *errmsg;
1101 int rc, err = SMB_NIC_SUCCESS;
1102
1103 sql = sqlite_mprintf("DELETE FROM hosts WHERE hostname = '%s'", host);
1104 if (sql == NULL)
1105 return (SMB_NIC_NO_MEMORY);
1106
1107 db = smb_nic_dbopen(SMB_NIC_DB_ORW);
1108 if (db == NULL) {
1109 sqlite_freemem(sql);
1110 return (SMB_NIC_DBOPEN_FAILED);
1111 }
1112
1113 rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1114 sqlite_freemem(sql);
1115 smb_nic_dbclose(db);
1116
1117 if (rc != SQLITE_OK) {
1118 syslog(LOG_ERR, "Failed to delete host %s from host " \
1119 "database (%s).", host, NULL_MSGCHK(errmsg));
1120 sqlite_freemem(errmsg);
1121 err = SMB_NIC_DELETE_FAILED;
1122 }
1123
1124 return (err);
1125 }
1126
1127 /*
1128 * smb_nic_dbsetinfo
1129 *
1130 * Initializes the db_info table upon database creation.
1131 */
1132 static int
smb_nic_dbsetinfo(sqlite * db)1133 smb_nic_dbsetinfo(sqlite *db)
1134 {
1135 char *errmsg = NULL;
1136 char *sql;
1137 int rc, err = SMB_NIC_SUCCESS;
1138
1139 sql = sqlite_mprintf("INSERT INTO db_info (ver_major, ver_minor,"
1140 " magic) VALUES (%d, %d, %d)", SMB_NIC_DB_VERMAJOR,
1141 SMB_NIC_DB_VERMINOR, SMB_NIC_DB_MAGIC);
1142
1143 if (sql == NULL)
1144 return (SMB_NIC_NO_MEMORY);
1145
1146 rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1147 sqlite_freemem(sql);
1148 if (rc != SQLITE_OK) {
1149 syslog(LOG_ERR, "Failed to add database information to " \
1150 "host database (%s).", NULL_MSGCHK(errmsg));
1151 sqlite_freemem(errmsg);
1152 err = SMB_NIC_DBINIT_ERROR;
1153 }
1154
1155 return (err);
1156 }
1157
1158 /*
1159 * smb_nic_nbt_get_exclude_list
1160 *
1161 * Construct an array containing list of i/f names on which NetBIOS traffic is
1162 * to be disabled, from a string containing a list of comma separated i/f names.
1163 *
1164 * Returns the number of i/f on which NetBIOS traffic is to be disabled.
1165 */
1166 static int
smb_nic_nbt_get_exclude_list(char * excludestr,char ** iflist,int max_nifs)1167 smb_nic_nbt_get_exclude_list(char *excludestr, char **iflist, int max_nifs)
1168 {
1169 int n = 0;
1170 char *entry;
1171
1172 bzero(iflist, SMB_PI_MAX_NETWORKS * sizeof (char *));
1173
1174 (void) trim_whitespace(excludestr);
1175 (void) strcanon(excludestr, ",");
1176
1177 if (*excludestr == '\0')
1178 return (0);
1179
1180 while (((iflist[n] = strsep(&excludestr, ",")) != NULL) &&
1181 (n < max_nifs)) {
1182 entry = iflist[n];
1183 if (*entry == '\0')
1184 continue;
1185 n++;
1186 }
1187
1188 return (n);
1189 }
1190
1191 /*
1192 * smb_nic_nbt_exclude
1193 *
1194 * Check to see if the given interface name should send NetBIOS traffic or not.
1195 *
1196 * Returns TRUE if NetBIOS traffic is disabled on an interface name.
1197 * Returns FALSE otherwise.
1198 */
1199 static boolean_t
smb_nic_nbt_exclude(const smb_nic_t * nc,const char ** exclude_list,int nexclude)1200 smb_nic_nbt_exclude(const smb_nic_t *nc, const char **exclude_list,
1201 int nexclude)
1202 {
1203 char buf[INET6_ADDRSTRLEN];
1204 const char *ifname = nc->nic_ifname;
1205 int i;
1206
1207 if (inet_ntop(AF_INET, &nc->nic_ip, buf, INET6_ADDRSTRLEN) == NULL)
1208 buf[0] = '\0';
1209
1210 for (i = 0; i < nexclude; i++) {
1211 if (strcmp(ifname, exclude_list[i]) == 0)
1212 return (B_TRUE);
1213
1214 if ((buf[0] != '\0') && (strcmp(buf, exclude_list[i]) == 0))
1215 return (B_TRUE);
1216 }
1217
1218 return (B_FALSE);
1219 }
1220