xref: /illumos-gate/usr/src/lib/smbsrv/libsmb/common/smb_scfutil.c (revision 45818ee124adeaaf947698996b4f4c722afc6d1f)
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 2014 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  */
49 static void
50 smb_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  */
63 int
64 smb_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  */
112 int
113 smb_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  */
154 int
155 smb_smf_end_transaction(smb_scfhandle_t *handle)
156 {
157 	int ret = SMBD_SMF_OK;
158 
159 	if (handle == NULL)
160 		return (SMBD_SMF_SYSTEM_ERR);
161 
162 	if (handle->scf_trans == NULL) {
163 		ret = SMBD_SMF_SYSTEM_ERR;
164 	} else {
165 		if (scf_transaction_commit(handle->scf_trans) < 0) {
166 			ret = SMBD_SMF_SYSTEM_ERR;
167 			smb_smf_scf_log_error("Failed to commit "
168 			    "transaction: %s");
169 		}
170 		scf_transaction_destroy_children(handle->scf_trans);
171 		scf_transaction_destroy(handle->scf_trans);
172 		handle->scf_trans = NULL;
173 	}
174 	return (ret);
175 }
176 
177 /*
178  * Sets string property in current pg
179  */
180 int
181 smb_smf_set_string_property(smb_scfhandle_t *handle,
182     char *propname, char *valstr)
183 {
184 	int ret = SMBD_SMF_OK;
185 	scf_value_t *value = NULL;
186 	scf_transaction_entry_t *entry = NULL;
187 
188 	if (handle == NULL)
189 		return (SMBD_SMF_SYSTEM_ERR);
190 
191 	/*
192 	 * properties must be set in transactions and don't take
193 	 * effect until the transaction has been ended/committed.
194 	 */
195 	value = scf_value_create(handle->scf_handle);
196 	entry = scf_entry_create(handle->scf_handle);
197 	if (value != NULL && entry != NULL) {
198 		if (scf_transaction_property_change(handle->scf_trans, entry,
199 		    propname, SCF_TYPE_ASTRING) == 0 ||
200 		    scf_transaction_property_new(handle->scf_trans, entry,
201 		    propname, SCF_TYPE_ASTRING) == 0) {
202 			if (scf_value_set_astring(value, valstr) == 0) {
203 				if (scf_entry_add_value(entry, value) != 0) {
204 					ret = SMBD_SMF_SYSTEM_ERR;
205 					scf_value_destroy(value);
206 				}
207 				/* the value is in the transaction */
208 				value = NULL;
209 			} else {
210 				/* value couldn't be constructed */
211 				ret = SMBD_SMF_SYSTEM_ERR;
212 			}
213 			/* the entry is in the transaction */
214 			entry = NULL;
215 		} else {
216 			ret = SMBD_SMF_SYSTEM_ERR;
217 		}
218 	} else {
219 		ret = SMBD_SMF_SYSTEM_ERR;
220 	}
221 	if (ret == SMBD_SMF_SYSTEM_ERR) {
222 		switch (scf_error()) {
223 		case SCF_ERROR_PERMISSION_DENIED:
224 			ret = SMBD_SMF_NO_PERMISSION;
225 			break;
226 		}
227 	}
228 
229 	/*
230 	 * cleanup if there were any errors that didn't leave these
231 	 * values where they would be cleaned up later.
232 	 */
233 	if (value != NULL)
234 		scf_value_destroy(value);
235 	if (entry != NULL)
236 		scf_entry_destroy(entry);
237 	return (ret);
238 }
239 
240 /*
241  * Gets string property value.upto sz size.
242  * Caller is responsible to have enough memory allocated.
243  */
244 int
245 smb_smf_get_string_property(smb_scfhandle_t *handle, char *propname,
246     char *valstr, size_t sz)
247 {
248 	int ret = SMBD_SMF_OK;
249 	scf_value_t *value;
250 	scf_property_t *prop;
251 
252 	if (handle == NULL)
253 		return (SMBD_SMF_SYSTEM_ERR);
254 
255 	value = scf_value_create(handle->scf_handle);
256 	prop = scf_property_create(handle->scf_handle);
257 	if (value && prop &&
258 	    (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) {
259 		if (scf_property_get_value(prop, value) == 0) {
260 			if (scf_value_get_astring(value, valstr, sz) < 0) {
261 				ret = SMBD_SMF_SYSTEM_ERR;
262 			}
263 		} else {
264 			ret = SMBD_SMF_SYSTEM_ERR;
265 		}
266 	} else {
267 		ret = SMBD_SMF_SYSTEM_ERR;
268 	}
269 	if (value != NULL)
270 		scf_value_destroy(value);
271 	if (prop != NULL)
272 		scf_property_destroy(prop);
273 	return (ret);
274 }
275 
276 /*
277  * Set integer value of property.
278  * The value is returned as int64_t value
279  * Caller ensures appropriate translation.
280  */
281 int
282 smb_smf_set_integer_property(smb_scfhandle_t *handle, char *propname,
283     int64_t valint)
284 {
285 	int ret = SMBD_SMF_OK;
286 	scf_value_t *value = NULL;
287 	scf_transaction_entry_t *entry = NULL;
288 
289 	if (handle == NULL)
290 		return (SMBD_SMF_SYSTEM_ERR);
291 
292 	/*
293 	 * properties must be set in transactions and don't take
294 	 * effect until the transaction has been ended/committed.
295 	 */
296 	value = scf_value_create(handle->scf_handle);
297 	entry = scf_entry_create(handle->scf_handle);
298 	if (value != NULL && entry != NULL) {
299 		if (scf_transaction_property_change(handle->scf_trans, entry,
300 		    propname, SCF_TYPE_INTEGER) == 0 ||
301 		    scf_transaction_property_new(handle->scf_trans, entry,
302 		    propname, SCF_TYPE_INTEGER) == 0) {
303 			scf_value_set_integer(value, valint);
304 			if (scf_entry_add_value(entry, value) != 0) {
305 				ret = SMBD_SMF_SYSTEM_ERR;
306 				scf_value_destroy(value);
307 			}
308 			/* the value is in the transaction */
309 			value = NULL;
310 		}
311 		/* the entry is in the transaction */
312 		entry = NULL;
313 	} else {
314 		ret = SMBD_SMF_SYSTEM_ERR;
315 	}
316 	if (ret == SMBD_SMF_SYSTEM_ERR) {
317 		switch (scf_error()) {
318 		case SCF_ERROR_PERMISSION_DENIED:
319 			ret = SMBD_SMF_NO_PERMISSION;
320 			break;
321 		}
322 	}
323 	/*
324 	 * cleanup if there were any errors that didn't leave these
325 	 * values where they would be cleaned up later.
326 	 */
327 	if (value != NULL)
328 		scf_value_destroy(value);
329 	if (entry != NULL)
330 		scf_entry_destroy(entry);
331 	return (ret);
332 }
333 
334 /*
335  * Gets integer property value.
336  * Caller is responsible to have enough memory allocated.
337  */
338 int
339 smb_smf_get_integer_property(smb_scfhandle_t *handle, char *propname,
340     int64_t *valint)
341 {
342 	int ret = SMBD_SMF_OK;
343 	scf_value_t *value = NULL;
344 	scf_property_t *prop = NULL;
345 
346 	if (handle == NULL)
347 		return (SMBD_SMF_SYSTEM_ERR);
348 
349 	value = scf_value_create(handle->scf_handle);
350 	prop = scf_property_create(handle->scf_handle);
351 	if ((prop) && (value) &&
352 	    (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) {
353 		if (scf_property_get_value(prop, value) == 0) {
354 			if (scf_value_get_integer(value,
355 			    valint) != 0) {
356 				ret = SMBD_SMF_SYSTEM_ERR;
357 			}
358 		} else {
359 			ret = SMBD_SMF_SYSTEM_ERR;
360 		}
361 	} else {
362 		ret = SMBD_SMF_SYSTEM_ERR;
363 	}
364 	if (value != NULL)
365 		scf_value_destroy(value);
366 	if (prop != NULL)
367 		scf_property_destroy(prop);
368 	return (ret);
369 }
370 
371 /*
372  * Set boolean value of property.
373  * The value is returned as int64_t value
374  * Caller ensures appropriate translation.
375  */
376 int
377 smb_smf_set_boolean_property(smb_scfhandle_t *handle, char *propname,
378     uint8_t valbool)
379 {
380 	int ret = SMBD_SMF_OK;
381 	scf_value_t *value = NULL;
382 	scf_transaction_entry_t *entry = NULL;
383 
384 	if (handle == NULL)
385 		return (SMBD_SMF_SYSTEM_ERR);
386 
387 	/*
388 	 * properties must be set in transactions and don't take
389 	 * effect until the transaction has been ended/committed.
390 	 */
391 	value = scf_value_create(handle->scf_handle);
392 	entry = scf_entry_create(handle->scf_handle);
393 	if (value != NULL && entry != NULL) {
394 		if (scf_transaction_property_change(handle->scf_trans, entry,
395 		    propname, SCF_TYPE_BOOLEAN) == 0 ||
396 		    scf_transaction_property_new(handle->scf_trans, entry,
397 		    propname, SCF_TYPE_BOOLEAN) == 0) {
398 			scf_value_set_boolean(value, valbool);
399 			if (scf_entry_add_value(entry, value) != 0) {
400 				ret = SMBD_SMF_SYSTEM_ERR;
401 				scf_value_destroy(value);
402 			}
403 			/* the value is in the transaction */
404 			value = NULL;
405 		}
406 		/* the entry is in the transaction */
407 		entry = NULL;
408 	} else {
409 		ret = SMBD_SMF_SYSTEM_ERR;
410 	}
411 	if (ret == SMBD_SMF_SYSTEM_ERR) {
412 		switch (scf_error()) {
413 		case SCF_ERROR_PERMISSION_DENIED:
414 			ret = SMBD_SMF_NO_PERMISSION;
415 			break;
416 		}
417 	}
418 	/*
419 	 * cleanup if there were any errors that didn't leave these
420 	 * values where they would be cleaned up later.
421 	 */
422 	if (value != NULL)
423 		scf_value_destroy(value);
424 	if (entry != NULL)
425 		scf_entry_destroy(entry);
426 	return (ret);
427 }
428 
429 /*
430  * Gets boolean property value.
431  * Caller is responsible to have enough memory allocated.
432  */
433 int
434 smb_smf_get_boolean_property(smb_scfhandle_t *handle, char *propname,
435     uint8_t *valbool)
436 {
437 	int ret = SMBD_SMF_OK;
438 	scf_value_t *value = NULL;
439 	scf_property_t *prop = NULL;
440 
441 	if (handle == NULL)
442 		return (SMBD_SMF_SYSTEM_ERR);
443 
444 	value = scf_value_create(handle->scf_handle);
445 	prop = scf_property_create(handle->scf_handle);
446 	if ((prop) && (value) &&
447 	    (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) {
448 		if (scf_property_get_value(prop, value) == 0) {
449 			if (scf_value_get_boolean(value,
450 			    valbool) != 0) {
451 				ret = SMBD_SMF_SYSTEM_ERR;
452 			}
453 		} else {
454 			ret = SMBD_SMF_SYSTEM_ERR;
455 		}
456 	} else {
457 		ret = SMBD_SMF_SYSTEM_ERR;
458 	}
459 	if (value != NULL)
460 		scf_value_destroy(value);
461 	if (prop != NULL)
462 		scf_property_destroy(prop);
463 	return (ret);
464 }
465 
466 /*
467  * Sets a blob property value.
468  */
469 int
470 smb_smf_set_opaque_property(smb_scfhandle_t *handle, char *propname,
471     void *voidval, size_t sz)
472 {
473 	int ret = SMBD_SMF_OK;
474 	scf_value_t *value;
475 	scf_transaction_entry_t *entry;
476 
477 	if (handle == NULL)
478 		return (SMBD_SMF_SYSTEM_ERR);
479 
480 	/*
481 	 * properties must be set in transactions and don't take
482 	 * effect until the transaction has been ended/committed.
483 	 */
484 	value = scf_value_create(handle->scf_handle);
485 	entry = scf_entry_create(handle->scf_handle);
486 	if (value != NULL && entry != NULL) {
487 		if (scf_transaction_property_change(handle->scf_trans, entry,
488 		    propname, SCF_TYPE_OPAQUE) == 0 ||
489 		    scf_transaction_property_new(handle->scf_trans, entry,
490 		    propname, SCF_TYPE_OPAQUE) == 0) {
491 			if (scf_value_set_opaque(value, voidval, sz) == 0) {
492 				if (scf_entry_add_value(entry, value) != 0) {
493 					ret = SMBD_SMF_SYSTEM_ERR;
494 					scf_value_destroy(value);
495 				}
496 				/* the value is in the transaction */
497 				value = NULL;
498 			} else {
499 				/* value couldn't be constructed */
500 				ret = SMBD_SMF_SYSTEM_ERR;
501 			}
502 			/* the entry is in the transaction */
503 			entry = NULL;
504 		} else {
505 			ret = SMBD_SMF_SYSTEM_ERR;
506 		}
507 	} else {
508 		ret = SMBD_SMF_SYSTEM_ERR;
509 	}
510 	if (ret == SMBD_SMF_SYSTEM_ERR) {
511 		switch (scf_error()) {
512 		case SCF_ERROR_PERMISSION_DENIED:
513 			ret = SMBD_SMF_NO_PERMISSION;
514 			break;
515 		}
516 	}
517 	/*
518 	 * cleanup if there were any errors that didn't leave these
519 	 * values where they would be cleaned up later.
520 	 */
521 	if (value != NULL)
522 		scf_value_destroy(value);
523 	if (entry != NULL)
524 		scf_entry_destroy(entry);
525 	return (ret);
526 }
527 
528 /*
529  * Gets a blob property value.
530  * Caller is responsible to have enough memory allocated.
531  */
532 int
533 smb_smf_get_opaque_property(smb_scfhandle_t *handle, char *propname,
534     void *v, size_t sz)
535 {
536 	int ret = SMBD_SMF_OK;
537 	scf_value_t *value = NULL;
538 	scf_property_t *prop = NULL;
539 
540 	if (handle == NULL)
541 		return (SMBD_SMF_SYSTEM_ERR);
542 
543 	value = scf_value_create(handle->scf_handle);
544 	prop = scf_property_create(handle->scf_handle);
545 	if ((prop) && (value) &&
546 	    (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) {
547 		if (scf_property_get_value(prop, value) == 0) {
548 			if (scf_value_get_opaque(value, (char *)v, sz) != sz) {
549 				ret = SMBD_SMF_SYSTEM_ERR;
550 			}
551 		} else {
552 			ret = SMBD_SMF_SYSTEM_ERR;
553 		}
554 	} else {
555 		ret = SMBD_SMF_SYSTEM_ERR;
556 	}
557 	if (value != NULL)
558 		scf_value_destroy(value);
559 	if (prop != NULL)
560 		scf_property_destroy(prop);
561 	return (ret);
562 }
563 
564 /*
565  * Put the smb service into maintenance mode.
566  */
567 int
568 smb_smf_maintenance_mode(void)
569 {
570 	return (smf_maintain_instance(SMBD_DEFAULT_INSTANCE_FMRI, 0));
571 }
572 
573 /*
574  * Restart the smb service.
575  */
576 int
577 smb_smf_restart_service(void)
578 {
579 	return (smf_restart_instance(SMBD_DEFAULT_INSTANCE_FMRI));
580 }
581 
582 /*
583  * smb_smf_scf_init()
584  *
585  * must be called before using any of the SCF functions.
586  * Returns smb_scfhandle_t pointer if success.
587  */
588 smb_scfhandle_t *
589 smb_smf_scf_init(char *svc_name)
590 {
591 	smb_scfhandle_t *handle;
592 
593 	handle = malloc(sizeof (smb_scfhandle_t));
594 	if (handle != NULL) {
595 		bzero((char *)handle, sizeof (smb_scfhandle_t));
596 		handle->scf_state = SCH_STATE_INITIALIZING;
597 		handle->scf_handle = scf_handle_create(SCF_VERSION);
598 		if (handle->scf_handle != NULL) {
599 			if (scf_handle_bind(handle->scf_handle) == 0) {
600 				handle->scf_scope =
601 				    scf_scope_create(handle->scf_handle);
602 
603 				if (handle->scf_scope == NULL)
604 					goto err;
605 
606 				if (scf_handle_get_local_scope(
607 				    handle->scf_handle, handle->scf_scope) != 0)
608 					goto err;
609 
610 				handle->scf_service =
611 				    scf_service_create(handle->scf_handle);
612 
613 				if (handle->scf_service == NULL)
614 					goto err;
615 
616 				if (scf_scope_get_service(handle->scf_scope,
617 				    svc_name, handle->scf_service)
618 				    != SCF_SUCCESS) {
619 					goto err;
620 				}
621 				handle->scf_pg =
622 				    scf_pg_create(handle->scf_handle);
623 
624 				if (handle->scf_pg == NULL)
625 					goto err;
626 
627 				handle->scf_state = SCH_STATE_INIT;
628 			} else {
629 				goto err;
630 			}
631 		} else {
632 			free(handle);
633 			handle = NULL;
634 			smb_smf_scf_log_error("Could not access SMF "
635 			    "repository: %s\n");
636 		}
637 	}
638 	return (handle);
639 
640 	/* error handling/unwinding */
641 err:
642 	(void) smb_smf_scf_fini(handle);
643 	(void) smb_smf_scf_log_error("SMF initialization problem: %s\n");
644 	return (NULL);
645 }
646 
647 /*
648  * smb_smf_scf_fini(handle)
649  *
650  * must be called when done. Called with the handle allocated in
651  * smb_smf_scf_init(), it cleans up the state and frees any SCF resources
652  * still in use.
653  */
654 void
655 smb_smf_scf_fini(smb_scfhandle_t *handle)
656 {
657 	if (handle != NULL) {
658 		int unbind = 0;
659 		scf_iter_destroy(handle->scf_pg_iter);
660 		handle->scf_pg_iter = NULL;
661 
662 		scf_iter_destroy(handle->scf_inst_iter);
663 		handle->scf_inst_iter = NULL;
664 
665 		unbind = 1;
666 		scf_scope_destroy(handle->scf_scope);
667 		handle->scf_scope = NULL;
668 
669 		scf_instance_destroy(handle->scf_instance);
670 		handle->scf_instance = NULL;
671 
672 		scf_service_destroy(handle->scf_service);
673 		handle->scf_service = NULL;
674 
675 		scf_pg_destroy(handle->scf_pg);
676 		handle->scf_pg = NULL;
677 
678 		handle->scf_state = SCH_STATE_UNINIT;
679 		if (unbind)
680 			(void) scf_handle_unbind(handle->scf_handle);
681 		scf_handle_destroy(handle->scf_handle);
682 		handle->scf_handle = NULL;
683 
684 		free(handle);
685 	}
686 }
687