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