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