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 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <sys/types.h>
27#include <sys/acctctl.h>
28#include <unistd.h>
29#include <string.h>
30#include <stdlib.h>
31#include <errno.h>
32#include <limits.h>
33#include <libdllink.h>
34#include <libscf.h>
35#include <pwd.h>
36#include <auth_attr.h>
37#include <nss_dbdefs.h>
38#include <secdb.h>
39#include <priv.h>
40#include <zone.h>
41
42#include "aconf.h"
43#include "utils.h"
44#include "res.h"
45
46#define	FMRI_FLOW_ACCT	"svc:/system/extended-accounting:flow"
47#define	FMRI_PROC_ACCT	"svc:/system/extended-accounting:process"
48#define	FMRI_TASK_ACCT	"svc:/system/extended-accounting:task"
49#define	FMRI_NET_ACCT	"svc:/system/extended-accounting:net"
50
51#define	NELEM(x)	(sizeof (x)) / (sizeof (x[0]))
52
53typedef struct props {
54	char *propname;
55	int proptype;
56	scf_transaction_entry_t *entry;
57	scf_value_t *value;
58	struct props *next;
59} props_t;
60
61static void	aconf_print_type(acctconf_t *, FILE *, int);
62static int	aconf_get_bool(const char *, const char *, uint8_t *);
63static int	aconf_get_string(const char *, const char *, char *, size_t);
64static props_t	*aconf_prop(const char *, int);
65static int	aconf_fmri2type(const char *);
66
67static scf_handle_t	*handle = NULL;
68static scf_instance_t	*inst = NULL;
69static props_t		*props = NULL;
70
71void
72aconf_init(acctconf_t *acp, int type)
73{
74	void *buf;
75	char *tracked;
76	char *untracked;
77
78	if ((buf = malloc(AC_BUFSIZE)) == NULL)
79		die(gettext("not enough memory\n"));
80
81	if (acctctl(type | AC_STATE_GET, &acp->state,
82	    sizeof (acp->state)) == -1)
83		die(gettext("cannot get %s accounting state\n"),
84		    ac_type_name(type));
85
86	(void) memset(acp->file, 0, sizeof (acp->file));
87	if (acctctl(type | AC_FILE_GET, acp->file, sizeof (acp->file)) == -1) {
88		if (errno == ENOTACTIVE)
89			(void) strlcpy(acp->file, AC_STR_NONE,
90			    sizeof (acp->file));
91		else
92			die(gettext("cannot get %s accounting file name"),
93			    ac_type_name(type));
94	}
95	(void) memset(buf, 0, AC_BUFSIZE);
96	if (acctctl(type | AC_RES_GET, buf, AC_BUFSIZE) == -1)
97		die(gettext("cannot obtain the list of enabled resources\n"));
98
99	tracked = buf2str(buf, AC_BUFSIZE, AC_ON, type);
100	untracked = buf2str(buf, AC_BUFSIZE, AC_OFF, type);
101	(void) strlcpy(acp->tracked, tracked, sizeof (acp->tracked));
102	(void) strlcpy(acp->untracked, untracked, sizeof (acp->untracked));
103	free(tracked);
104	free(untracked);
105	free(buf);
106}
107
108/*
109 * SMF start method: configure extended accounting from properties stored in
110 * the repository.  Any errors encountered while retrieving properties from
111 * the repository, such as missing properties or properties of the wrong type,
112 * are fatal as they indicate severe damage to the service (all required
113 * properties are delivered in the service manifest and should thus always be
114 * present).  No attempts will be made to repair such damage;  the service will
115 * be forced into maintenance state by returning SMF_EXIT_ERR_CONFIG.  For all
116 * other errors we we try to configure as much as possible and return
117 * SMF_EXIT_ERR_FATAL.
118 */
119int
120aconf_setup(const char *fmri)
121{
122	char file[MAXPATHLEN];
123	char tracked[MAXRESLEN];
124	char untracked[MAXRESLEN];
125	void *buf;
126	int type;
127	int state;
128	uint8_t b;
129	int ret = SMF_EXIT_OK;
130
131	if ((type = aconf_fmri2type(fmri)) == -1) {
132		warn(gettext("no accounting type for %s\n"), fmri);
133		return (SMF_EXIT_ERR_FATAL);
134	}
135
136	/*
137	 * Net/Flow accounting is not available in non-global zones and
138	 * the service instance should therefore never be 'enabled' in
139	 * non-global zones.  This is enforced by acctadm(1M), but there is
140	 * nothing that prevents someone from calling svcadm enable directly,
141	 * so we handle that case here by disabling the instance.
142	 */
143	if ((type == AC_FLOW || type == AC_NET) &&
144	    getzoneid() != GLOBAL_ZONEID) {
145		(void) smf_disable_instance(fmri, 0);
146		warn(gettext("%s accounting cannot be configured in "
147		    "non-global zones\n"), ac_type_name(type));
148		return (SMF_EXIT_OK);
149	}
150
151	if (aconf_scf_init(fmri) == -1) {
152		warn(gettext("cannot connect to repository\n"));
153		return (SMF_EXIT_ERR_FATAL);
154	}
155	if (aconf_get_string(AC_PGNAME, AC_PROP_TRACKED, tracked,
156	    sizeof (tracked)) == -1) {
157		warn(gettext("cannot get %s property\n"), AC_PROP_TRACKED);
158		ret = SMF_EXIT_ERR_CONFIG;
159		goto out;
160	}
161	if (aconf_get_string(AC_PGNAME, AC_PROP_UNTRACKED, untracked,
162	    sizeof (untracked)) == -1) {
163		warn(gettext("cannot get %s property\n"), AC_PROP_UNTRACKED);
164		ret = SMF_EXIT_ERR_CONFIG;
165		goto out;
166	}
167	if (aconf_get_string(AC_PGNAME, AC_PROP_FILE, file,
168	    sizeof (file)) == -1) {
169		warn(gettext("cannot get %s property\n"), AC_PROP_FILE);
170		ret = SMF_EXIT_ERR_CONFIG;
171		goto out;
172	}
173	if (aconf_get_bool(AC_PGNAME, AC_PROP_STATE, &b) == -1) {
174		warn(gettext("cannot get %s property\n"), AC_PROP_STATE);
175		ret = SMF_EXIT_ERR_CONFIG;
176		goto out;
177	}
178	state = (b ? AC_ON : AC_OFF);
179
180	if ((buf = malloc(AC_BUFSIZE)) == NULL) {
181		warn(gettext("not enough memory\n"));
182		ret = SMF_EXIT_ERR_FATAL;
183		goto out;
184	}
185	(void) memset(buf, 0, AC_BUFSIZE);
186	str2buf(buf, untracked, AC_OFF, type);
187	str2buf(buf, tracked, AC_ON, type);
188
189	(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
190	if (acctctl(type | AC_RES_SET, buf, AC_BUFSIZE) == -1) {
191		warn(gettext("cannot enable/disable %s accounting resources"),
192		    ac_type_name(type));
193		ret = SMF_EXIT_ERR_FATAL;
194	}
195	free(buf);
196
197	if (strcmp(file, AC_STR_NONE) != 0) {
198		if (open_exacct_file(file, type) == -1)
199			ret = SMF_EXIT_ERR_FATAL;
200	} else {
201		if (acctctl(type | AC_FILE_SET, NULL, 0) == -1) {
202			warn(gettext("cannot close %s accounting file"),
203			    ac_type_name(type));
204			ret = SMF_EXIT_ERR_FATAL;
205		}
206	}
207	if (acctctl(type | AC_STATE_SET, &state, sizeof (state)) == -1) {
208		warn(gettext("cannot %s %s accounting"),
209		    state == AC_ON ? gettext("enable") : gettext("disable"),
210		    ac_type_name(type));
211		ret = SMF_EXIT_ERR_FATAL;
212	}
213	(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
214
215	if (state == AC_ON && type == AC_NET) {
216		/*
217		 * Start logging.
218		 */
219		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_DL_CONFIG,
220		    NULL);
221		(void) dladm_start_usagelog(dld_handle,
222		    strncmp(tracked, "basic", strlen("basic")) == 0 ?
223		    DLADM_LOGTYPE_LINK : DLADM_LOGTYPE_FLOW, 20);
224		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_DL_CONFIG,
225		    NULL);
226	}
227out:
228	aconf_scf_fini();
229	return (ret);
230}
231
232void
233aconf_print(FILE *fp, int types)
234{
235	acctconf_t ac;
236	int print_order[] = { AC_TASK, AC_PROC, AC_FLOW, AC_NET };
237	int i;
238
239	for (i = 0; i < NELEM(print_order); i++) {
240		if (types & print_order[i]) {
241			aconf_init(&ac, print_order[i]);
242			aconf_print_type(&ac, fp, print_order[i]);
243		}
244	}
245}
246
247static void
248aconf_print_type(acctconf_t *acp, FILE *fp, int type)
249{
250	switch (type) {
251	case AC_TASK:
252		(void) fprintf(fp,
253		    gettext("            Task accounting: %s\n"),
254		    acp->state == AC_ON ?
255		    gettext("active") : gettext("inactive"));
256		(void) fprintf(fp,
257		    gettext("       Task accounting file: %s\n"),
258		    acp->file);
259		(void) fprintf(fp,
260		    gettext("     Tracked task resources: %s\n"),
261		    acp->tracked);
262		(void) fprintf(fp,
263		    gettext("   Untracked task resources: %s\n"),
264		    acp->untracked);
265		break;
266	case AC_PROC:
267		(void) fprintf(fp,
268		    gettext("         Process accounting: %s\n"),
269		    acp->state == AC_ON ?
270		    gettext("active") : gettext("inactive"));
271		(void) fprintf(fp,
272		    gettext("    Process accounting file: %s\n"),
273		    acp->file);
274		(void) fprintf(fp,
275		    gettext("  Tracked process resources: %s\n"),
276		    acp->tracked);
277		(void) fprintf(fp,
278		    gettext("Untracked process resources: %s\n"),
279		    acp->untracked);
280		break;
281	case AC_FLOW:
282		(void) fprintf(fp,
283		    gettext("            Flow accounting: %s\n"),
284		    acp->state == AC_ON ?
285		    gettext("active") : gettext("inactive"));
286		(void) fprintf(fp,
287		    gettext("       Flow accounting file: %s\n"),
288		    acp->file);
289		(void) fprintf(fp,
290		    gettext("     Tracked flow resources: %s\n"),
291		    acp->tracked);
292		(void) fprintf(fp,
293		    gettext("   Untracked flow resources: %s\n"),
294		    acp->untracked);
295		break;
296	case AC_NET:
297		(void) fprintf(fp,
298		    gettext("            Net accounting: %s\n"),
299		    acp->state == AC_ON ?
300		    gettext("active") : gettext("inactive"));
301		(void) fprintf(fp,
302		    gettext("       Net accounting file: %s\n"),
303		    acp->file);
304		(void) fprintf(fp,
305		    gettext("     Tracked net resources: %s\n"),
306		    acp->tracked);
307		(void) fprintf(fp,
308		    gettext("   Untracked net resources: %s\n"),
309		    acp->untracked);
310		break;
311	}
312}
313
314/*
315 * Modified properties are put on the 'props' linked list by aconf_set_string()
316 * and aconf_set_bool().  Walk the list of modified properties and write them
317 * to the repository.  The list is deleted on exit.
318 */
319int
320aconf_save(void)
321{
322	scf_propertygroup_t *pg;
323	scf_transaction_t *tx;
324	props_t *p;
325	props_t *q;
326	int tx_result;
327
328	if (props == NULL)
329		return (0);
330
331	if ((pg = scf_pg_create(handle)) == NULL ||
332	    scf_instance_get_pg(inst, AC_PGNAME, pg) == -1 ||
333	    (tx = scf_transaction_create(handle)) == NULL)
334		goto out;
335
336	do {
337		if (scf_pg_update(pg) == -1 ||
338		    scf_transaction_start(tx, pg) == -1)
339			goto out;
340
341		for (p = props; p != NULL; p = p->next) {
342			if (scf_transaction_property_change(tx, p->entry,
343			    p->propname, p->proptype) == -1)
344				goto out;
345			(void) scf_entry_add_value(p->entry, p->value);
346		}
347		tx_result = scf_transaction_commit(tx);
348		scf_transaction_reset(tx);
349	} while (tx_result == 0);
350
351out:
352	p = props;
353	while (p != NULL) {
354		scf_value_destroy(p->value);
355		scf_entry_destroy(p->entry);
356		free(p->propname);
357		q = p->next;
358		free(p);
359		p = q;
360	}
361	props = NULL;
362	scf_transaction_destroy(tx);
363	scf_pg_destroy(pg);
364	return ((tx_result == 1) ? 0 : -1);
365}
366
367boolean_t
368aconf_have_smf_auths(void)
369{
370	char auth[NSS_BUFLEN_AUTHATTR];
371	struct passwd *pw;
372
373	if ((pw = getpwuid(getuid())) == NULL)
374		return (B_FALSE);
375
376	if (aconf_get_string("general", "action_authorization", auth,
377	    sizeof (auth)) == -1 || chkauthattr(auth, pw->pw_name) == 0)
378		return (B_FALSE);
379
380	if (aconf_get_string("general", "value_authorization", auth,
381	    sizeof (auth)) == -1 || chkauthattr(auth, pw->pw_name) == 0)
382		return (B_FALSE);
383
384	if (aconf_get_string("config", "value_authorization", auth,
385	    sizeof (auth)) == -1 || chkauthattr(auth, pw->pw_name) == 0)
386		return (B_FALSE);
387
388	return (B_TRUE);
389}
390
391const char *
392aconf_type2fmri(int type)
393{
394	switch (type) {
395	case AC_PROC:
396		return (FMRI_PROC_ACCT);
397	case AC_TASK:
398		return (FMRI_TASK_ACCT);
399	case AC_FLOW:
400		return (FMRI_FLOW_ACCT);
401	case AC_NET:
402		return (FMRI_NET_ACCT);
403	default:
404		die(gettext("invalid type %d\n"), type);
405	}
406	/* NOTREACHED */
407	return (NULL);
408}
409
410static int
411aconf_fmri2type(const char *fmri)
412{
413	if (strcmp(fmri, FMRI_PROC_ACCT) == 0)
414		return (AC_PROC);
415	else if (strcmp(fmri, FMRI_TASK_ACCT) == 0)
416		return (AC_TASK);
417	else if (strcmp(fmri, FMRI_FLOW_ACCT) == 0)
418		return (AC_FLOW);
419	else if (strcmp(fmri, FMRI_NET_ACCT) == 0)
420		return (AC_NET);
421	else
422		return (-1);
423}
424
425int
426aconf_scf_init(const char *fmri)
427{
428	if ((handle = scf_handle_create(SCF_VERSION)) == NULL ||
429	    scf_handle_bind(handle) == -1 ||
430	    (inst = scf_instance_create(handle)) == NULL ||
431	    scf_handle_decode_fmri(handle, fmri, NULL, NULL, inst, NULL, NULL,
432	    SCF_DECODE_FMRI_EXACT) == -1) {
433		aconf_scf_fini();
434		return (-1);
435	}
436	return (0);
437}
438
439void
440aconf_scf_fini(void)
441{
442	scf_instance_destroy(inst);
443	(void) scf_handle_unbind(handle);
444	scf_handle_destroy(handle);
445}
446
447static int
448aconf_get_string(const char *pgname, const char *propname, char *buf,
449    size_t len)
450{
451	scf_propertygroup_t *pg;
452	scf_property_t *prop;
453	scf_value_t *value;
454	int ret = 0;
455
456	if ((pg = scf_pg_create(handle)) == NULL)
457		return (-1);
458
459	if (scf_instance_get_pg_composed(inst, NULL, pgname, pg) == -1) {
460		scf_pg_destroy(pg);
461		return (-1);
462	}
463
464	if ((prop = scf_property_create(handle)) == NULL ||
465	    (value = scf_value_create(handle)) == NULL ||
466	    scf_pg_get_property(pg, propname, prop) == -1 ||
467	    scf_property_get_value(prop, value) == -1 ||
468	    scf_value_get_astring(value, buf, len) == -1)
469		ret = -1;
470
471	scf_value_destroy(value);
472	scf_property_destroy(prop);
473	scf_pg_destroy(pg);
474	return (ret);
475}
476
477static int
478aconf_get_bool(const char *pgname, const char *propname, uint8_t *rval)
479{
480	scf_propertygroup_t *pg;
481	scf_property_t *prop;
482	scf_value_t *value;
483	int ret = 0;
484
485	if ((pg = scf_pg_create(handle)) == NULL)
486		return (-1);
487
488	if (scf_instance_get_pg_composed(inst, NULL, pgname, pg) == -1) {
489		scf_pg_destroy(pg);
490		return (-1);
491	}
492
493	if ((prop = scf_property_create(handle)) == NULL ||
494	    (value = scf_value_create(handle)) == NULL ||
495	    scf_pg_get_property(pg, propname, prop) == -1 ||
496	    scf_property_get_value(prop, value) == -1 ||
497	    scf_value_get_boolean(value, rval) == -1)
498		ret = -1;
499
500	scf_value_destroy(value);
501	scf_property_destroy(prop);
502	scf_pg_destroy(pg);
503	return (ret);
504}
505
506int
507aconf_set_string(const char *propname, const char *value)
508{
509	props_t *p;
510
511	if ((p = aconf_prop(propname, SCF_TYPE_ASTRING)) == NULL)
512		return (-1);
513
514	if (scf_value_set_astring(p->value, value) == -1)
515		return (-1);
516	return (0);
517}
518
519int
520aconf_set_bool(const char *propname, boolean_t value)
521{
522	props_t *p;
523
524	if ((p = aconf_prop(propname, SCF_TYPE_BOOLEAN)) == NULL)
525		return (-1);
526
527	scf_value_set_boolean(p->value, value);
528	return (0);
529}
530
531static props_t *
532aconf_prop(const char *propname, int proptype)
533{
534	props_t *p;
535
536	if ((p = malloc(sizeof (props_t))) != NULL) {
537		if ((p->propname = strdup(propname)) == NULL) {
538			free(p);
539			return (NULL);
540		}
541		if ((p->entry = scf_entry_create(handle)) == NULL) {
542			free(p->propname);
543			free(p);
544			return (NULL);
545		}
546		if ((p->value = scf_value_create(handle)) == NULL) {
547			scf_entry_destroy(p->entry);
548			free(p->propname);
549			free(p);
550			return (NULL);
551		}
552		p->proptype = proptype;
553		p->next = props;
554		props = p;
555	}
556	return (p);
557}
558