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