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