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/*
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26/*
27 * util.c contains a set of miscellaneous utility functions which,
28 * among other things:
29 * - start a child process
30 * - look up the zone name
31 * - look up/set SMF properties
32 * - drop/escalate privs
33 */
34
35#include <assert.h>
36#include <errno.h>
37#include <libdllink.h>
38#include <limits.h>
39#include <libscf.h>
40#include <net/if.h>
41#include <pthread.h>
42#include <pwd.h>
43#include <spawn.h>
44#include <stdarg.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48#include <strings.h>
49#include <stropts.h>
50#include <sys/socket.h>
51#include <sys/sockio.h>
52#include <sys/types.h>
53#include <unistd.h>
54#include <wait.h>
55#include <zone.h>
56
57#include "util.h"
58#include "llp.h"
59
60extern char **environ;
61extern sigset_t original_sigmask;
62
63/*
64 * A holder for all the resources needed to get a property value
65 * using libscf.
66 */
67typedef struct scf_resources {
68	scf_handle_t *sr_handle;
69	scf_instance_t *sr_inst;
70	scf_snapshot_t *sr_snap;
71	scf_propertygroup_t *sr_pg;
72	scf_property_t *sr_prop;
73	scf_value_t *sr_val;
74	scf_transaction_t *sr_tx;
75	scf_transaction_entry_t *sr_ent;
76} scf_resources_t;
77
78static pthread_mutex_t uid_mutex = PTHREAD_MUTEX_INITIALIZER;
79static uid_t uid;
80static int uid_cnt;
81
82void
83nwamd_escalate(void) {
84	priv_set_t *priv_set;
85	priv_set = priv_str_to_set("zone", ",", NULL);
86
87	if (priv_set == NULL)
88		pfail("creating privilege set: %s", strerror(errno));
89
90	(void) pthread_mutex_lock(&uid_mutex);
91	if (uid == 0)
92		uid = getuid();
93	if (uid_cnt++ == 0) {
94		if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) {
95			priv_freeset(priv_set);
96			pfail("setppriv effective: %s", strerror(errno));
97		}
98	}
99	(void) pthread_mutex_unlock(&uid_mutex);
100
101	priv_freeset(priv_set);
102}
103
104void
105nwamd_deescalate(void) {
106	(void) pthread_mutex_lock(&uid_mutex);
107
108	assert(uid_cnt > 0);
109	if (--uid_cnt == 0) {
110		priv_set_t *priv_set, *allpriv_set;
111
112		/* build up our minimal set of privs. */
113		priv_set = priv_str_to_set("basic", ",", NULL);
114		allpriv_set = priv_str_to_set("zone", ",", NULL);
115		if (priv_set == NULL || allpriv_set == NULL)
116			pfail("converting privilege sets: %s", strerror(errno));
117
118		(void) priv_addset(priv_set, PRIV_FILE_CHOWN_SELF);
119		(void) priv_addset(priv_set, PRIV_FILE_DAC_READ);
120		(void) priv_addset(priv_set, PRIV_FILE_DAC_WRITE);
121		(void) priv_addset(priv_set, PRIV_NET_RAWACCESS);
122		(void) priv_addset(priv_set, PRIV_NET_PRIVADDR);
123		(void) priv_addset(priv_set, PRIV_PROC_AUDIT);
124		(void) priv_addset(priv_set, PRIV_PROC_OWNER);
125		(void) priv_addset(priv_set, PRIV_PROC_SETID);
126		(void) priv_addset(priv_set, PRIV_SYS_CONFIG);
127		(void) priv_addset(priv_set, PRIV_SYS_IP_CONFIG);
128		(void) priv_addset(priv_set, PRIV_SYS_IPC_CONFIG);
129		(void) priv_addset(priv_set, PRIV_SYS_MOUNT);
130		(void) priv_addset(priv_set, PRIV_SYS_NET_CONFIG);
131		(void) priv_addset(priv_set, PRIV_SYS_RES_CONFIG);
132		(void) priv_addset(priv_set, PRIV_SYS_RESOURCE);
133
134		/*
135		 * Since our zone might not have all these privs,
136		 * just ask for those that are available.
137		 */
138		priv_intersect(allpriv_set, priv_set);
139
140		if (setppriv(PRIV_SET, PRIV_INHERITABLE, priv_set) == -1) {
141			priv_freeset(allpriv_set);
142			priv_freeset(priv_set);
143			pfail("setppriv inheritable: %s", strerror(errno));
144		}
145		/*
146		 * Need to ensure permitted set contains all privs so we can
147		 * escalate later.
148		 */
149		if (setppriv(PRIV_SET, PRIV_PERMITTED, allpriv_set) == -1) {
150			priv_freeset(allpriv_set);
151			priv_freeset(priv_set);
152			pfail("setppriv permitted: %s", strerror(errno));
153		}
154		/*
155		 * We need to find a smaller set of privs that are important to
156		 * us.  Otherwise we really are not gaining much by doing this.
157		 */
158		if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) {
159			priv_freeset(allpriv_set);
160			priv_freeset(priv_set);
161			pfail("setppriv effective: %s", strerror(errno));
162		}
163
164		priv_freeset(priv_set);
165		priv_freeset(allpriv_set);
166	}
167	(void) pthread_mutex_unlock(&uid_mutex);
168}
169
170/*
171 *
172 * This starts a child process determined by command.  If command contains a
173 * slash then it is assumed to be a full path; otherwise the path is searched
174 * for an executable file with the name command.  Command is also used as
175 * argv[0] of the new process.  The rest of the arguments of the function
176 * up to the first NULL make up pointers to arguments of the new process.
177 *
178 * This function returns child exit status on success and -1 on failure.
179 *
180 * NOTE: original_sigmask must be set before this function is called.
181 */
182int
183nwamd_start_childv(const char *command, char const * const *argv)
184{
185	posix_spawnattr_t attr;
186	sigset_t fullset;
187	int i, rc, status, n;
188	pid_t pid;
189	char vbuf[1024];
190
191	vbuf[0] = 0;
192	n = sizeof (vbuf);
193	for (i = 1; argv[i] != NULL && n > 2; i++) {
194		n -= strlcat(vbuf, " ", n);
195		n -= strlcat(vbuf, argv[i], n);
196	}
197	if (argv[i] != NULL || n < 0)
198		nlog(LOG_ERR, "nwamd_start_childv can't log full arg vector");
199
200	if ((rc = posix_spawnattr_init(&attr)) != 0) {
201		nlog(LOG_DEBUG, "posix_spawnattr_init %d %s\n",
202		    rc, strerror(rc));
203		return (-1);
204	}
205	(void) sigfillset(&fullset);
206	if ((rc = posix_spawnattr_setsigdefault(&attr, &fullset)) != 0) {
207		nlog(LOG_DEBUG, "setsigdefault %d %s\n", rc, strerror(rc));
208		return (-1);
209	}
210	if ((rc = posix_spawnattr_setsigmask(&attr, &original_sigmask)) != 0) {
211		nlog(LOG_DEBUG, "setsigmask %d %s\n", rc, strerror(rc));
212		return (-1);
213	}
214	if ((rc = posix_spawnattr_setflags(&attr,
215	    POSIX_SPAWN_SETSIGDEF|POSIX_SPAWN_SETSIGMASK)) != 0) {
216		nlog(LOG_DEBUG, "setflags %d %s\n", rc, strerror(rc));
217		return (-1);
218	}
219
220	if ((rc = posix_spawnp(&pid, command, NULL, &attr, (char * const *)argv,
221	    environ)) > 0) {
222		nlog(LOG_DEBUG, "posix_spawnp failed errno %d", rc);
223		return (-1);
224	}
225
226	if ((rc = posix_spawnattr_destroy(&attr)) != 0) {
227		nlog(LOG_DEBUG, "posix_spawn_attr_destroy %d %s\n",
228		    rc, strerror(rc));
229		return (-1);
230	}
231
232	(void) waitpid(pid, &status, 0);
233	if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
234		i = WIFSIGNALED(status) ? WTERMSIG(status) : WSTOPSIG(status);
235		nlog(LOG_ERR, "'%s%s' %s with signal %d (%s)", command, vbuf,
236		    (WIFSIGNALED(status) ? "terminated" : "stopped"), i,
237		    strsignal(i));
238		return (-2);
239	} else {
240		nlog(LOG_INFO, "'%s%s' completed normally: %d", command, vbuf,
241		    WEXITSTATUS(status));
242		return (WEXITSTATUS(status));
243	}
244}
245
246/*
247 * For global zone, check if the link is used by a non-global
248 * zone, note that the non-global zones doesn't need this check,
249 * because zoneadm has taken care of this when the zone boots.
250 * In the global zone, we ignore events for local-zone-owned links
251 * since these are taken care of by the local zone's network
252 * configuration services.
253 */
254boolean_t
255nwamd_link_belongs_to_this_zone(const char *linkname)
256{
257	zoneid_t zoneid;
258	char zonename[ZONENAME_MAX];
259	int ret;
260
261	zoneid = getzoneid();
262	if (zoneid == GLOBAL_ZONEID) {
263		datalink_id_t linkid;
264		dladm_status_t status;
265		char errstr[DLADM_STRSIZE];
266
267		if ((status = dladm_name2info(dld_handle, linkname, &linkid,
268		    NULL, NULL, NULL)) != DLADM_STATUS_OK) {
269			nlog(LOG_DEBUG, "nwamd_link_belongs_to_this_zone: "
270			    "could not get linkid for %s: %s",
271			    linkname, dladm_status2str(status, errstr));
272			return (B_FALSE);
273		}
274		zoneid = ALL_ZONES;
275		ret = zone_check_datalink(&zoneid, linkid);
276		if (ret == 0) {
277			(void) getzonenamebyid(zoneid, zonename, ZONENAME_MAX);
278			nlog(LOG_DEBUG, "nwamd_link_belongs_to_this_zone: "
279			    "%s is used by non-global zone: %s",
280			    linkname, zonename);
281			return (B_FALSE);
282		}
283	}
284	return (B_TRUE);
285}
286
287/*
288 * Inputs:
289 *   res is a pointer to the scf_resources_t to be released.
290 */
291static void
292release_scf_resources(scf_resources_t *res)
293{
294	scf_entry_destroy(res->sr_ent);
295	scf_transaction_destroy(res->sr_tx);
296	scf_value_destroy(res->sr_val);
297	scf_property_destroy(res->sr_prop);
298	scf_pg_destroy(res->sr_pg);
299	scf_snapshot_destroy(res->sr_snap);
300	scf_instance_destroy(res->sr_inst);
301	(void) scf_handle_unbind(res->sr_handle);
302	scf_handle_destroy(res->sr_handle);
303}
304
305/*
306 * Inputs:
307 *   fmri is the instance to look up
308 * Outputs:
309 *   res is a pointer to an scf_resources_t.  This is an internal
310 *   structure that holds all the handles needed to get a specific
311 *   property from the running snapshot; on a successful return it
312 *   contains the scf_value_t that should be passed to the desired
313 *   scf_value_get_foo() function, and must be freed after use by
314 *   calling release_scf_resources().  On a failure return, any
315 *   resources that may have been assigned to res are released, so
316 *   the caller does not need to do any cleanup in the failure case.
317 * Returns:
318 *    0 on success
319 *   -1 on failure
320 */
321
322static int
323create_scf_resources(const char *fmri, scf_resources_t *res)
324{
325	res->sr_tx = NULL;
326	res->sr_ent = NULL;
327	res->sr_inst = NULL;
328	res->sr_snap = NULL;
329	res->sr_pg = NULL;
330	res->sr_prop = NULL;
331	res->sr_val = NULL;
332
333	if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL) {
334		return (-1);
335	}
336
337	if (scf_handle_bind(res->sr_handle) != 0) {
338		scf_handle_destroy(res->sr_handle);
339		return (-1);
340	}
341	if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL) {
342		goto failure;
343	}
344	if (scf_handle_decode_fmri(res->sr_handle, fmri, NULL, NULL,
345	    res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
346		goto failure;
347	}
348	if ((res->sr_snap = scf_snapshot_create(res->sr_handle)) == NULL) {
349		goto failure;
350	}
351	if (scf_instance_get_snapshot(res->sr_inst, "running",
352	    res->sr_snap) != 0) {
353		goto failure;
354	}
355	if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) {
356		goto failure;
357	}
358	if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL) {
359		goto failure;
360	}
361	if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL) {
362		goto failure;
363	}
364	if ((res->sr_tx = scf_transaction_create(res->sr_handle)) == NULL) {
365		goto failure;
366	}
367	if ((res->sr_ent = scf_entry_create(res->sr_handle)) == NULL) {
368		goto failure;
369	}
370	return (0);
371
372failure:
373	nlog(LOG_ERR, "create_scf_resources failed: %s",
374	    scf_strerror(scf_error()));
375	release_scf_resources(res);
376	return (-1);
377}
378
379/*
380 * Inputs:
381 *   fmri is the instance to look up
382 *   pg is the property group to look up
383 *   prop is the property within that group to look up
384 *   running specifies if running snapshot is to be used
385 * Outputs:
386 *   res is a pointer to an scf_resources_t.  This is an internal
387 *   structure that holds all the handles needed to get a specific
388 *   property from the running snapshot; on a successful return it
389 *   contains the scf_value_t that should be passed to the desired
390 *   scf_value_get_foo() function, and must be freed after use by
391 *   calling release_scf_resources().  On a failure return, any
392 *   resources that may have been assigned to res are released, so
393 *   the caller does not need to do any cleanup in the failure case.
394 * Returns:
395 *    0 on success
396 *   -1 on failure
397 */
398static int
399get_property_value(const char *fmri, const char *pg, const char *prop,
400    boolean_t running, scf_resources_t *res)
401{
402	if (create_scf_resources(fmri, res) != 0)
403		return (-1);
404
405	if (scf_instance_get_pg_composed(res->sr_inst,
406	    running ? res->sr_snap : NULL, pg, res->sr_pg) != 0) {
407		goto failure;
408	}
409	if (scf_pg_get_property(res->sr_pg, prop, res->sr_prop) != 0) {
410		goto failure;
411	}
412	if (scf_property_get_value(res->sr_prop, res->sr_val) != 0) {
413		goto failure;
414	}
415	return (0);
416
417failure:
418	release_scf_resources(res);
419	return (-1);
420}
421
422/*
423 * Inputs:
424 *   lfmri is the instance fmri to look up
425 *   lpg is the property group to look up
426 *   lprop is the property within that group to look up
427 * Outputs:
428 *   answer is a pointer to the property value
429 * Returns:
430 *    0 on success
431 *   -1 on failure
432 * If successful, the property value is retured in *answer.
433 * Otherwise, *answer is undefined, and it is up to the caller to decide
434 * how to handle that case.
435 */
436int
437nwamd_lookup_boolean_property(const char *lfmri, const char *lpg,
438    const char *lprop, boolean_t *answer)
439{
440	int result = -1;
441	scf_resources_t res;
442	uint8_t prop_val;
443
444	if (get_property_value(lfmri, lpg, lprop, B_TRUE, &res) != 0) {
445
446		/*
447		 * an error was already logged by get_property_value,
448		 * and it released any resources assigned to res before
449		 * returning.
450		 */
451		return (result);
452	}
453	if (scf_value_get_boolean(res.sr_val, &prop_val) != 0) {
454		goto cleanup;
455	}
456	*answer = (boolean_t)prop_val;
457	result = 0;
458cleanup:
459	release_scf_resources(&res);
460	return (result);
461}
462
463/*
464 * Inputs:
465 *   lfmri is the instance fmri to look up
466 *   lpg is the property group to look up
467 *   lprop is the property within that group to look up
468 *   buf is the place to put the answer
469 *   bufsz is the size of buf
470 * Outputs:
471 *
472 * Returns:
473 *    0 on success
474 *   -1 on failure
475 * If successful, the property value is retured in buf.
476 * Otherwise, buf is undefined, and it is up to the caller to decide
477 * how to handle that case.
478 */
479int
480nwamd_lookup_string_property(const char *lfmri, const char *lpg,
481    const char *lprop, char *buf, size_t bufsz)
482{
483	int result = -1;
484	scf_resources_t res;
485
486	if (get_property_value(lfmri, lpg, lprop, B_TRUE, &res) != 0) {
487		/*
488		 * The above function fails when trying to get a
489		 * non-persistent property group from the running snapshot.
490		 * Try going for the non-running snapshot.
491		 */
492		if (get_property_value(lfmri, lpg, lprop, B_FALSE, &res) != 0) {
493			/*
494			 * an error was already logged by get_property_value,
495			 * and it released any resources assigned to res before
496			 * returning.
497			 */
498			return (result);
499		}
500	}
501	if (scf_value_get_astring(res.sr_val, buf, bufsz) == 0)
502		goto cleanup;
503
504	result = 0;
505cleanup:
506	release_scf_resources(&res);
507	return (result);
508}
509
510/*
511 * Inputs:
512 *   lfmri is the instance fmri to look up
513 *   lpg is the property group to look up
514 *   lprop is the property within that group to look up
515 * Outputs:
516 *   answer is a pointer to the property value
517 * Returns:
518 *    0 on success
519 *   -1 on failure
520 * If successful, the property value is retured in *answer.
521 * Otherwise, *answer is undefined, and it is up to the caller to decide
522 * how to handle that case.
523 */
524int
525nwamd_lookup_count_property(const char *lfmri, const char *lpg,
526    const char *lprop, uint64_t *answer)
527{
528	int result = -1;
529	scf_resources_t res;
530
531	if (get_property_value(lfmri, lpg, lprop, B_TRUE, &res) != 0) {
532
533		/*
534		 * an error was already logged by get_property_value,
535		 * and it released any resources assigned to res before
536		 * returning.
537		 */
538		return (result);
539	}
540	if (scf_value_get_count(res.sr_val, answer) != 0) {
541		goto cleanup;
542	}
543	result = 0;
544cleanup:
545	release_scf_resources(&res);
546	return (result);
547}
548
549static int
550set_property_value(scf_resources_t *res, const char *propname,
551    scf_type_t proptype)
552{
553	int result = -1;
554	boolean_t new;
555
556retry:
557	new = (scf_pg_get_property(res->sr_pg, propname, res->sr_prop) != 0);
558
559	if (scf_transaction_start(res->sr_tx, res->sr_pg) == -1) {
560		goto failure;
561	}
562	if (new) {
563		if (scf_transaction_property_new(res->sr_tx, res->sr_ent,
564		    propname, proptype) == -1) {
565			goto failure;
566		}
567	} else {
568		if (scf_transaction_property_change(res->sr_tx, res->sr_ent,
569		    propname, proptype) == -1) {
570			goto failure;
571		}
572	}
573
574	if (scf_entry_add_value(res->sr_ent, res->sr_val) != 0) {
575		goto failure;
576	}
577
578	result = scf_transaction_commit(res->sr_tx);
579	if (result == 0) {
580		scf_transaction_reset(res->sr_tx);
581		if (scf_pg_update(res->sr_pg) == -1) {
582			goto failure;
583		}
584		nlog(LOG_INFO, "set_property_value: transaction commit failed "
585		    "for %s; retrying", propname);
586		goto retry;
587	}
588	if (result == -1)
589		goto failure;
590	return (0);
591
592failure:
593	return (-1);
594}
595
596int
597nwamd_set_count_property(const char *fmri, const char *pg, const char *prop,
598    uint64_t count)
599{
600	scf_resources_t res;
601
602	if (create_scf_resources(fmri, &res) != 0)
603		return (-1);
604
605	if (scf_instance_add_pg(res.sr_inst, pg, SCF_GROUP_APPLICATION,
606	    SCF_PG_FLAG_NONPERSISTENT, res.sr_pg) != 0) {
607		if (scf_error() != SCF_ERROR_EXISTS)
608			goto failure;
609		if (scf_instance_get_pg_composed(res.sr_inst, NULL, pg,
610		    res.sr_pg) != 0)
611			goto failure;
612	}
613
614	scf_value_set_count(res.sr_val, (uint64_t)count);
615
616	if (set_property_value(&res, prop, SCF_TYPE_COUNT) != 0)
617		goto failure;
618
619	release_scf_resources(&res);
620	return (0);
621
622failure:
623	nlog(LOG_INFO, "nwamd_set_count_property: scf failure %s while "
624	    "setting %s", scf_strerror(scf_error()), prop);
625	release_scf_resources(&res);
626	return (-1);
627}
628
629int
630nwamd_set_string_property(const char *fmri, const char *pg, const char *prop,
631    const char *str)
632{
633	scf_resources_t res;
634
635	if (create_scf_resources(fmri, &res) != 0)
636		return (-1);
637
638	if (scf_instance_add_pg(res.sr_inst, pg, SCF_GROUP_APPLICATION,
639	    SCF_PG_FLAG_NONPERSISTENT, res.sr_pg) != 0) {
640		if (scf_error() != SCF_ERROR_EXISTS)
641			goto failure;
642		if (scf_instance_get_pg_composed(res.sr_inst, NULL, pg,
643		    res.sr_pg) != 0)
644			goto failure;
645	}
646
647	if (scf_value_set_astring(res.sr_val, str) != 0)
648		goto failure;
649
650	if (set_property_value(&res, prop, SCF_TYPE_ASTRING) != 0)
651		goto failure;
652
653	release_scf_resources(&res);
654	return (0);
655
656failure:
657	nlog(LOG_INFO, "nwamd_set_string_property: scf failure %s while "
658	    "setting %s", scf_strerror(scf_error()), prop);
659	release_scf_resources(&res);
660	return (-1);
661}
662
663/*
664 * Deletes property prop from property group pg in SMF instance fmri.
665 * Returns 0 on success, -1 on failure.
666 */
667int
668nwamd_delete_scf_property(const char *fmri, const char *pg, const char *prop)
669{
670	scf_resources_t res;
671	int result = -1;
672
673	if (create_scf_resources(fmri, &res) != 0)
674		return (-1);
675
676	if (scf_instance_add_pg(res.sr_inst, pg, SCF_GROUP_APPLICATION,
677	    SCF_PG_FLAG_NONPERSISTENT, res.sr_pg) != 0) {
678		if (scf_error() != SCF_ERROR_EXISTS)
679			goto failure;
680		if (scf_instance_get_pg_composed(res.sr_inst, NULL, pg,
681		    res.sr_pg) != 0)
682			goto failure;
683	}
684
685	if (scf_pg_get_property(res.sr_pg, prop, res.sr_prop) != 0)
686		goto failure;
687retry:
688	if (scf_transaction_start(res.sr_tx, res.sr_pg) == -1)
689		goto failure;
690
691	if (scf_transaction_property_delete(res.sr_tx, res.sr_ent, prop) == -1)
692		goto failure;
693
694	result = scf_transaction_commit(res.sr_tx);
695	if (result == 0) {
696		scf_transaction_reset(res.sr_tx);
697		if (scf_pg_update(res.sr_pg) == -1)
698			goto failure;
699		goto retry;
700	}
701	if (result == -1)
702		goto failure;
703
704	release_scf_resources(&res);
705	return (0);
706failure:
707	release_scf_resources(&res);
708	return (-1);
709}
710