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 * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
26 */
27
28/* helper functions for using libscf with CIFS */
29
30#include <libscf.h>
31#include <string.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <syslog.h>
35#include <errno.h>
36#include <libintl.h>
37#include <assert.h>
38#include <strings.h>
39
40#include <uuid/uuid.h>
41#include <sys/param.h>
42
43#include <smbsrv/libsmb.h>
44
45/*
46 * smb_smf_scf_log_error(msg)
47 * Logs error messages from scf API's
48 */
49static void
50smb_smf_scf_log_error(char *msg)
51{
52	if (msg == NULL)
53		msg = "SMBD SMF problem";
54
55	syslog(LOG_ERR, " %s: %s", msg, scf_strerror(scf_error()));
56}
57
58/*
59 * smb_smf_create_service_pgroup(handle, pgroup)
60 *
61 * create a new property group at service level.
62 */
63int
64smb_smf_create_service_pgroup(smb_scfhandle_t *handle, char *pgroup)
65{
66	int ret = SMBD_SMF_OK;
67	int err;
68
69	if (handle == NULL)
70		return (SMBD_SMF_SYSTEM_ERR);
71
72	/*
73	 * only create a handle if it doesn't exist. It is ok to exist
74	 * since the pg handle will be set as a side effect.
75	 */
76	if (handle->scf_pg == NULL)
77		if ((handle->scf_pg =
78		    scf_pg_create(handle->scf_handle)) == NULL)
79			return (SMBD_SMF_SYSTEM_ERR);
80
81	/*
82	 * if the pgroup exists, we are done. If it doesn't, then we
83	 * need to actually add one to the service instance.
84	 */
85	if (scf_service_get_pg(handle->scf_service,
86	    pgroup, handle->scf_pg) != 0) {
87		/* doesn't exist so create one */
88		if (scf_service_add_pg(handle->scf_service, pgroup,
89		    SCF_GROUP_APPLICATION, 0, handle->scf_pg) != 0) {
90			err = scf_error();
91			if (err != SCF_ERROR_NONE)
92				smb_smf_scf_log_error(NULL);
93			switch (err) {
94			case SCF_ERROR_PERMISSION_DENIED:
95				ret = SMBD_SMF_NO_PERMISSION;
96				break;
97			default:
98				ret = SMBD_SMF_SYSTEM_ERR;
99				break;
100			}
101		}
102	}
103	return (ret);
104}
105
106/*
107 * Start transaction on current pg in handle.
108 * The pg could be service or instance level.
109 * Must be called after pg handle is obtained
110 * from create or get.
111 */
112int
113smb_smf_start_transaction(smb_scfhandle_t *handle)
114{
115	int ret = SMBD_SMF_OK;
116
117	if (!handle || (!handle->scf_pg))
118		return (SMBD_SMF_SYSTEM_ERR);
119
120	/*
121	 * lookup the property group and create it if it doesn't already
122	 * exist.
123	 */
124	if (handle->scf_state == SCH_STATE_INIT) {
125		if (ret == SMBD_SMF_OK) {
126			handle->scf_trans =
127			    scf_transaction_create(handle->scf_handle);
128			if (handle->scf_trans != NULL) {
129				if (scf_transaction_start(handle->scf_trans,
130				    handle->scf_pg) != 0) {
131					ret = SMBD_SMF_SYSTEM_ERR;
132					scf_transaction_destroy(
133					    handle->scf_trans);
134					handle->scf_trans = NULL;
135				}
136			} else {
137				ret = SMBD_SMF_SYSTEM_ERR;
138			}
139		}
140	}
141	if (ret == SMBD_SMF_SYSTEM_ERR &&
142	    scf_error() == SCF_ERROR_PERMISSION_DENIED)
143		ret = SMBD_SMF_NO_PERMISSION;
144
145	return (ret);
146}
147
148/*
149 * smb_smf_end_transaction(handle)
150 *
151 * Commit the changes that were added to the transaction in the
152 * handle. Do all necessary cleanup.
153 */
154int
155smb_smf_end_transaction(smb_scfhandle_t *handle)
156{
157	int ret = SMBD_SMF_OK;
158	int rc;
159
160	if (handle == NULL)
161		return (SMBD_SMF_SYSTEM_ERR);
162
163	if (handle->scf_trans == NULL) {
164		ret = SMBD_SMF_SYSTEM_ERR;
165	} else {
166		rc = scf_transaction_commit(handle->scf_trans);
167		if (rc == 1) {
168			ret = SMBD_SMF_OK;
169		} else if (rc == 0) {
170			ret = SMBD_SMF_INVALID_ARG;
171			smb_smf_scf_log_error("Failed to commit, old pg: "
172			    "transaction: %s");
173		} else {
174			ret = SMBD_SMF_SYSTEM_ERR;
175			smb_smf_scf_log_error("Failed to commit, error: "
176			    "transaction: %s");
177		}
178		scf_transaction_destroy_children(handle->scf_trans);
179		scf_transaction_destroy(handle->scf_trans);
180		handle->scf_trans = NULL;
181	}
182	return (ret);
183}
184
185/*
186 * Sets string property in current pg
187 */
188int
189smb_smf_set_string_property(smb_scfhandle_t *handle,
190    char *propname, char *valstr)
191{
192	int ret = SMBD_SMF_OK;
193	scf_value_t *value = NULL;
194	scf_transaction_entry_t *entry = NULL;
195
196	if (handle == NULL)
197		return (SMBD_SMF_SYSTEM_ERR);
198
199	/*
200	 * properties must be set in transactions and don't take
201	 * effect until the transaction has been ended/committed.
202	 */
203	value = scf_value_create(handle->scf_handle);
204	entry = scf_entry_create(handle->scf_handle);
205	if (value != NULL && entry != NULL) {
206		if (scf_transaction_property_change(handle->scf_trans, entry,
207		    propname, SCF_TYPE_ASTRING) == 0 ||
208		    scf_transaction_property_new(handle->scf_trans, entry,
209		    propname, SCF_TYPE_ASTRING) == 0) {
210			if (scf_value_set_astring(value, valstr) == 0) {
211				if (scf_entry_add_value(entry, value) != 0) {
212					ret = SMBD_SMF_SYSTEM_ERR;
213					scf_value_destroy(value);
214				}
215				/* the value is in the transaction */
216				value = NULL;
217			} else {
218				/* value couldn't be constructed */
219				ret = SMBD_SMF_SYSTEM_ERR;
220			}
221			/* the entry is in the transaction */
222			entry = NULL;
223		} else {
224			ret = SMBD_SMF_SYSTEM_ERR;
225		}
226	} else {
227		ret = SMBD_SMF_SYSTEM_ERR;
228	}
229	if (ret == SMBD_SMF_SYSTEM_ERR) {
230		switch (scf_error()) {
231		case SCF_ERROR_PERMISSION_DENIED:
232			ret = SMBD_SMF_NO_PERMISSION;
233			break;
234		}
235	}
236
237	/*
238	 * cleanup if there were any errors that didn't leave these
239	 * values where they would be cleaned up later.
240	 */
241	if (value != NULL)
242		scf_value_destroy(value);
243	if (entry != NULL)
244		scf_entry_destroy(entry);
245	return (ret);
246}
247
248/*
249 * Gets string property value.upto sz size.
250 * Caller is responsible to have enough memory allocated.
251 */
252int
253smb_smf_get_string_property(smb_scfhandle_t *handle, char *propname,
254    char *valstr, size_t sz)
255{
256	int ret = SMBD_SMF_OK;
257	scf_value_t *value;
258	scf_property_t *prop;
259
260	if (handle == NULL)
261		return (SMBD_SMF_SYSTEM_ERR);
262
263	value = scf_value_create(handle->scf_handle);
264	prop = scf_property_create(handle->scf_handle);
265	if (value && prop &&
266	    (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) {
267		if (scf_property_get_value(prop, value) == 0) {
268			if (scf_value_get_astring(value, valstr, sz) < 0) {
269				ret = SMBD_SMF_SYSTEM_ERR;
270			}
271		} else {
272			ret = SMBD_SMF_SYSTEM_ERR;
273		}
274	} else {
275		ret = SMBD_SMF_SYSTEM_ERR;
276	}
277	if (value != NULL)
278		scf_value_destroy(value);
279	if (prop != NULL)
280		scf_property_destroy(prop);
281	return (ret);
282}
283
284/*
285 * Set integer value of property.
286 * The value is returned as int64_t value
287 * Caller ensures appropriate translation.
288 */
289int
290smb_smf_set_integer_property(smb_scfhandle_t *handle, char *propname,
291    int64_t valint)
292{
293	int ret = SMBD_SMF_OK;
294	scf_value_t *value = NULL;
295	scf_transaction_entry_t *entry = NULL;
296
297	if (handle == NULL)
298		return (SMBD_SMF_SYSTEM_ERR);
299
300	/*
301	 * properties must be set in transactions and don't take
302	 * effect until the transaction has been ended/committed.
303	 */
304	value = scf_value_create(handle->scf_handle);
305	entry = scf_entry_create(handle->scf_handle);
306	if (value != NULL && entry != NULL) {
307		if (scf_transaction_property_change(handle->scf_trans, entry,
308		    propname, SCF_TYPE_INTEGER) == 0 ||
309		    scf_transaction_property_new(handle->scf_trans, entry,
310		    propname, SCF_TYPE_INTEGER) == 0) {
311			scf_value_set_integer(value, valint);
312			if (scf_entry_add_value(entry, value) != 0) {
313				ret = SMBD_SMF_SYSTEM_ERR;
314				scf_value_destroy(value);
315			}
316			/* the value is in the transaction */
317			value = NULL;
318		}
319		/* the entry is in the transaction */
320		entry = NULL;
321	} else {
322		ret = SMBD_SMF_SYSTEM_ERR;
323	}
324	if (ret == SMBD_SMF_SYSTEM_ERR) {
325		switch (scf_error()) {
326		case SCF_ERROR_PERMISSION_DENIED:
327			ret = SMBD_SMF_NO_PERMISSION;
328			break;
329		}
330	}
331	/*
332	 * cleanup if there were any errors that didn't leave these
333	 * values where they would be cleaned up later.
334	 */
335	if (value != NULL)
336		scf_value_destroy(value);
337	if (entry != NULL)
338		scf_entry_destroy(entry);
339	return (ret);
340}
341
342/*
343 * Gets integer property value.
344 * Caller is responsible to have enough memory allocated.
345 */
346int
347smb_smf_get_integer_property(smb_scfhandle_t *handle, char *propname,
348    int64_t *valint)
349{
350	int ret = SMBD_SMF_OK;
351	scf_value_t *value = NULL;
352	scf_property_t *prop = NULL;
353
354	if (handle == NULL)
355		return (SMBD_SMF_SYSTEM_ERR);
356
357	value = scf_value_create(handle->scf_handle);
358	prop = scf_property_create(handle->scf_handle);
359	if ((prop) && (value) &&
360	    (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) {
361		if (scf_property_get_value(prop, value) == 0) {
362			if (scf_value_get_integer(value,
363			    valint) != 0) {
364				ret = SMBD_SMF_SYSTEM_ERR;
365			}
366		} else {
367			ret = SMBD_SMF_SYSTEM_ERR;
368		}
369	} else {
370		ret = SMBD_SMF_SYSTEM_ERR;
371	}
372	if (value != NULL)
373		scf_value_destroy(value);
374	if (prop != NULL)
375		scf_property_destroy(prop);
376	return (ret);
377}
378
379/*
380 * Set boolean value of property.
381 * The value is returned as int64_t value
382 * Caller ensures appropriate translation.
383 */
384int
385smb_smf_set_boolean_property(smb_scfhandle_t *handle, char *propname,
386    uint8_t valbool)
387{
388	int ret = SMBD_SMF_OK;
389	scf_value_t *value = NULL;
390	scf_transaction_entry_t *entry = NULL;
391
392	if (handle == NULL)
393		return (SMBD_SMF_SYSTEM_ERR);
394
395	/*
396	 * properties must be set in transactions and don't take
397	 * effect until the transaction has been ended/committed.
398	 */
399	value = scf_value_create(handle->scf_handle);
400	entry = scf_entry_create(handle->scf_handle);
401	if (value != NULL && entry != NULL) {
402		if (scf_transaction_property_change(handle->scf_trans, entry,
403		    propname, SCF_TYPE_BOOLEAN) == 0 ||
404		    scf_transaction_property_new(handle->scf_trans, entry,
405		    propname, SCF_TYPE_BOOLEAN) == 0) {
406			scf_value_set_boolean(value, valbool);
407			if (scf_entry_add_value(entry, value) != 0) {
408				ret = SMBD_SMF_SYSTEM_ERR;
409				scf_value_destroy(value);
410			}
411			/* the value is in the transaction */
412			value = NULL;
413		}
414		/* the entry is in the transaction */
415		entry = NULL;
416	} else {
417		ret = SMBD_SMF_SYSTEM_ERR;
418	}
419	if (ret == SMBD_SMF_SYSTEM_ERR) {
420		switch (scf_error()) {
421		case SCF_ERROR_PERMISSION_DENIED:
422			ret = SMBD_SMF_NO_PERMISSION;
423			break;
424		}
425	}
426	/*
427	 * cleanup if there were any errors that didn't leave these
428	 * values where they would be cleaned up later.
429	 */
430	if (value != NULL)
431		scf_value_destroy(value);
432	if (entry != NULL)
433		scf_entry_destroy(entry);
434	return (ret);
435}
436
437/*
438 * Gets boolean property value.
439 * Caller is responsible to have enough memory allocated.
440 */
441int
442smb_smf_get_boolean_property(smb_scfhandle_t *handle, char *propname,
443    uint8_t *valbool)
444{
445	int ret = SMBD_SMF_OK;
446	scf_value_t *value = NULL;
447	scf_property_t *prop = NULL;
448
449	if (handle == NULL)
450		return (SMBD_SMF_SYSTEM_ERR);
451
452	value = scf_value_create(handle->scf_handle);
453	prop = scf_property_create(handle->scf_handle);
454	if ((prop) && (value) &&
455	    (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) {
456		if (scf_property_get_value(prop, value) == 0) {
457			if (scf_value_get_boolean(value,
458			    valbool) != 0) {
459				ret = SMBD_SMF_SYSTEM_ERR;
460			}
461		} else {
462			ret = SMBD_SMF_SYSTEM_ERR;
463		}
464	} else {
465		ret = SMBD_SMF_SYSTEM_ERR;
466	}
467	if (value != NULL)
468		scf_value_destroy(value);
469	if (prop != NULL)
470		scf_property_destroy(prop);
471	return (ret);
472}
473
474/*
475 * Sets a blob property value.
476 */
477int
478smb_smf_set_opaque_property(smb_scfhandle_t *handle, char *propname,
479    void *voidval, size_t sz)
480{
481	int ret = SMBD_SMF_OK;
482	scf_value_t *value;
483	scf_transaction_entry_t *entry;
484
485	if (handle == NULL)
486		return (SMBD_SMF_SYSTEM_ERR);
487
488	/*
489	 * properties must be set in transactions and don't take
490	 * effect until the transaction has been ended/committed.
491	 */
492	value = scf_value_create(handle->scf_handle);
493	entry = scf_entry_create(handle->scf_handle);
494	if (value != NULL && entry != NULL) {
495		if (scf_transaction_property_change(handle->scf_trans, entry,
496		    propname, SCF_TYPE_OPAQUE) == 0 ||
497		    scf_transaction_property_new(handle->scf_trans, entry,
498		    propname, SCF_TYPE_OPAQUE) == 0) {
499			if (scf_value_set_opaque(value, voidval, sz) == 0) {
500				if (scf_entry_add_value(entry, value) != 0) {
501					ret = SMBD_SMF_SYSTEM_ERR;
502					scf_value_destroy(value);
503				}
504				/* the value is in the transaction */
505				value = NULL;
506			} else {
507				/* value couldn't be constructed */
508				ret = SMBD_SMF_SYSTEM_ERR;
509			}
510			/* the entry is in the transaction */
511			entry = NULL;
512		} else {
513			ret = SMBD_SMF_SYSTEM_ERR;
514		}
515	} else {
516		ret = SMBD_SMF_SYSTEM_ERR;
517	}
518	if (ret == SMBD_SMF_SYSTEM_ERR) {
519		switch (scf_error()) {
520		case SCF_ERROR_PERMISSION_DENIED:
521			ret = SMBD_SMF_NO_PERMISSION;
522			break;
523		}
524	}
525	/*
526	 * cleanup if there were any errors that didn't leave these
527	 * values where they would be cleaned up later.
528	 */
529	if (value != NULL)
530		scf_value_destroy(value);
531	if (entry != NULL)
532		scf_entry_destroy(entry);
533	return (ret);
534}
535
536/*
537 * Gets a blob property value.
538 * Caller is responsible to have enough memory allocated.
539 */
540int
541smb_smf_get_opaque_property(smb_scfhandle_t *handle, char *propname,
542    void *v, size_t sz)
543{
544	int ret = SMBD_SMF_OK;
545	scf_value_t *value = NULL;
546	scf_property_t *prop = NULL;
547
548	if (handle == NULL)
549		return (SMBD_SMF_SYSTEM_ERR);
550
551	value = scf_value_create(handle->scf_handle);
552	prop = scf_property_create(handle->scf_handle);
553	if ((prop) && (value) &&
554	    (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) {
555		if (scf_property_get_value(prop, value) == 0) {
556			if (scf_value_get_opaque(value, (char *)v, sz) != sz) {
557				ret = SMBD_SMF_SYSTEM_ERR;
558			}
559		} else {
560			ret = SMBD_SMF_SYSTEM_ERR;
561		}
562	} else {
563		ret = SMBD_SMF_SYSTEM_ERR;
564	}
565	if (value != NULL)
566		scf_value_destroy(value);
567	if (prop != NULL)
568		scf_property_destroy(prop);
569	return (ret);
570}
571
572/*
573 * Delete a property (for properties obsoleted during an upgrade).
574 */
575int
576smb_smf_delete_property(smb_scfhandle_t *handle, char *propname)
577{
578	scf_transaction_entry_t *entry;
579	int ret = SMBD_SMF_OK;
580
581	if (handle == NULL)
582		return (SMBD_SMF_SYSTEM_ERR);
583	if (handle->scf_trans == NULL)
584		return (SMBD_SMF_SYSTEM_ERR);
585
586	/*
587	 * properties must be set in transactions and don't take
588	 * effect until the transaction has been ended/committed.
589	 */
590	entry = scf_entry_create(handle->scf_handle);
591	if (entry == NULL) {
592		ret = SMBD_SMF_SYSTEM_ERR;
593		goto out;
594	}
595
596	if (scf_transaction_property_delete(handle->scf_trans,
597	    entry, propname) == 0) {
598		/* the entry is in the transaction */
599		entry = NULL;
600	} else {
601		switch (scf_error()) {
602		case SCF_ERROR_NOT_FOUND:
603			/* Did not exist.  We're done. */
604			ret = SMBD_SMF_OK;
605			goto out;
606		case SCF_ERROR_PERMISSION_DENIED:
607			ret = SMBD_SMF_NO_PERMISSION;
608			goto out;
609		default:
610			ret = SMBD_SMF_SYSTEM_ERR;
611			goto out;
612		}
613	}
614
615out:
616	scf_entry_destroy(entry);
617	return (ret);
618}
619
620/*
621 * Put the smb service into maintenance mode.
622 */
623int
624smb_smf_maintenance_mode(void)
625{
626	return (smf_maintain_instance(SMBD_DEFAULT_INSTANCE_FMRI, 0));
627}
628
629/*
630 * Restart the smb service.
631 */
632int
633smb_smf_restart_service(void)
634{
635	return (smf_restart_instance(SMBD_DEFAULT_INSTANCE_FMRI));
636}
637
638/*
639 * smb_smf_scf_init()
640 *
641 * must be called before using any of the SCF functions.
642 * Returns smb_scfhandle_t pointer if success.
643 */
644smb_scfhandle_t *
645smb_smf_scf_init(char *svc_name)
646{
647	smb_scfhandle_t *handle;
648
649	handle = malloc(sizeof (smb_scfhandle_t));
650	if (handle != NULL) {
651		bzero((char *)handle, sizeof (smb_scfhandle_t));
652		handle->scf_state = SCH_STATE_INITIALIZING;
653		handle->scf_handle = scf_handle_create(SCF_VERSION);
654		if (handle->scf_handle != NULL) {
655			if (scf_handle_bind(handle->scf_handle) == 0) {
656				handle->scf_scope =
657				    scf_scope_create(handle->scf_handle);
658
659				if (handle->scf_scope == NULL)
660					goto err;
661
662				if (scf_handle_get_local_scope(
663				    handle->scf_handle, handle->scf_scope) != 0)
664					goto err;
665
666				handle->scf_service =
667				    scf_service_create(handle->scf_handle);
668
669				if (handle->scf_service == NULL)
670					goto err;
671
672				if (scf_scope_get_service(handle->scf_scope,
673				    svc_name, handle->scf_service)
674				    != SCF_SUCCESS) {
675					goto err;
676				}
677				handle->scf_pg =
678				    scf_pg_create(handle->scf_handle);
679
680				if (handle->scf_pg == NULL)
681					goto err;
682
683				handle->scf_state = SCH_STATE_INIT;
684			} else {
685				goto err;
686			}
687		} else {
688			free(handle);
689			handle = NULL;
690			smb_smf_scf_log_error("Could not access SMF "
691			    "repository: %s\n");
692		}
693	}
694	return (handle);
695
696	/* error handling/unwinding */
697err:
698	(void) smb_smf_scf_fini(handle);
699	(void) smb_smf_scf_log_error("SMF initialization problem: %s\n");
700	return (NULL);
701}
702
703/*
704 * smb_smf_scf_fini(handle)
705 *
706 * must be called when done. Called with the handle allocated in
707 * smb_smf_scf_init(), it cleans up the state and frees any SCF resources
708 * still in use.
709 */
710void
711smb_smf_scf_fini(smb_scfhandle_t *handle)
712{
713	if (handle != NULL) {
714		int unbind = 0;
715		scf_iter_destroy(handle->scf_pg_iter);
716		handle->scf_pg_iter = NULL;
717
718		scf_iter_destroy(handle->scf_inst_iter);
719		handle->scf_inst_iter = NULL;
720
721		unbind = 1;
722		scf_scope_destroy(handle->scf_scope);
723		handle->scf_scope = NULL;
724
725		scf_instance_destroy(handle->scf_instance);
726		handle->scf_instance = NULL;
727
728		scf_service_destroy(handle->scf_service);
729		handle->scf_service = NULL;
730
731		scf_pg_destroy(handle->scf_pg);
732		handle->scf_pg = NULL;
733
734		handle->scf_state = SCH_STATE_UNINIT;
735		if (unbind)
736			(void) scf_handle_unbind(handle->scf_handle);
737		scf_handle_destroy(handle->scf_handle);
738		handle->scf_handle = NULL;
739
740		free(handle);
741	}
742}
743