xref: /illumos-gate/usr/src/cmd/idmap/idmapd/idmap_config.c (revision fbd1c0dae6f4a2ccc2ce0527c7f19d3dd5ea90b8)
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 2007 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 /*
29  * Config routines common to idmap(1M) and idmapd(1M)
30  */
31 
32 #include <stdlib.h>
33 #include <synch.h>
34 #include <assert.h>
35 #include <sys/varargs.h>
36 #include <sys/systeminfo.h>
37 #include <strings.h>
38 #include <libintl.h>
39 #include <ctype.h>
40 #include <errno.h>
41 #include "idmapd.h"
42 #include <stdio.h>
43 #include <stdarg.h>
44 #include <uuid/uuid.h>
45 
46 #define	MACHINE_SID_LEN	(9 + UUID_LEN/4 * 11)
47 #define	FMRI_BASE "svc:/system/idmap"
48 #define	CONFIG_PG "config"
49 #define	GENERAL_PG "general"
50 /* initial length of the array for policy options/attributes: */
51 #define	DEF_ARRAY_LENGTH 16
52 
53 static const char *me = "idmapd";
54 
55 static int
56 generate_machine_sid(char **machine_sid) {
57 	char *p;
58 	uuid_t uu;
59 	int i, j, len, rlen;
60 	uint32_t rid;
61 
62 	/*
63 	 * Generate and split 128-bit UUID into four 32-bit RIDs
64 	 * The machine_sid will be of the form S-1-5-N1-N2-N3-N4
65 	 * We depart from Windows here, which instead of 128
66 	 * bits worth of random numbers uses 96 bits.
67 	 */
68 
69 	*machine_sid = calloc(1, MACHINE_SID_LEN);
70 	if (*machine_sid == NULL) {
71 		idmapdlog(LOG_ERR, "%s: Out of memory", me);
72 		return (-1);
73 	}
74 	(void) strcpy(*machine_sid, "S-1-5-21");
75 	p = *machine_sid + strlen("S-1-5-21");
76 	len = MACHINE_SID_LEN - strlen("S-1-5-21");
77 
78 	uuid_clear(uu);
79 	uuid_generate_random(uu);
80 
81 	for (i = 0; i < UUID_LEN/4; i++) {
82 		j = i * 4;
83 		rid = (uu[j] << 24) | (uu[j + 1] << 16) |
84 			(uu[j + 2] << 8) | (uu[j + 3]);
85 		rlen = snprintf(p, len, "-%u", rid);
86 		p += rlen;
87 		len -= rlen;
88 	}
89 
90 	return (0);
91 }
92 
93 /* Check if in the case of failure the original value of *val is preserved */
94 static int
95 get_val_int(idmap_cfg_t *cfg, char *name, void *val, scf_type_t type)
96 {
97 	int rc = 0;
98 
99 	scf_property_t *scf_prop = scf_property_create(cfg->handles.main);
100 	scf_value_t *value = scf_value_create(cfg->handles.main);
101 
102 
103 	if (0 > scf_pg_get_property(cfg->handles.config_pg, name, scf_prop))
104 	/* this is OK: the property is just undefined */
105 		goto destruction;
106 
107 
108 	if (0 > scf_property_get_value(scf_prop, value))
109 	/* It is still OK when a property doesn't have any value */
110 		goto destruction;
111 
112 	switch (type) {
113 	case SCF_TYPE_BOOLEAN:
114 		rc = scf_value_get_boolean(value, val);
115 		break;
116 	case SCF_TYPE_COUNT:
117 		rc = scf_value_get_count(value, val);
118 		break;
119 	case SCF_TYPE_INTEGER:
120 		rc = scf_value_get_integer(value, val);
121 		break;
122 	default:
123 		idmapdlog(LOG_ERR, "%s: Invalid scf integer type (%d)",
124 		    me, type);
125 		rc = -1;
126 		break;
127 	}
128 
129 
130 destruction:
131 	scf_value_destroy(value);
132 	scf_property_destroy(scf_prop);
133 
134 	return (rc);
135 }
136 
137 static char *
138 scf_value2string(scf_value_t *value) {
139 	int rc = -1;
140 	char buf_size = 127;
141 	int length;
142 	char *buf = NULL;
143 	buf = (char *) malloc(sizeof (char) * buf_size);
144 
145 	for (;;) {
146 		length = scf_value_get_astring(value, buf, buf_size);
147 		if (length < 0) {
148 			rc = -1;
149 			goto destruction;
150 		}
151 
152 		if (length == buf_size - 1) {
153 			buf_size *= 2;
154 			buf = (char *)realloc(buf, buf_size * sizeof (char));
155 			if (!buf) {
156 				idmapdlog(LOG_ERR, "%s: Out of memory", me);
157 				rc = -1;
158 				goto destruction;
159 			}
160 		} else {
161 			rc = 0;
162 			break;
163 		}
164 	}
165 
166 destruction:
167 	if (rc < 0) {
168 		if (buf)
169 			free(buf);
170 		buf = NULL;
171 	}
172 
173 	return (buf);
174 }
175 
176 
177 static int
178 get_val_astring(idmap_cfg_t *cfg, char *name, char **val)
179 {
180 	int rc = 0;
181 
182 	scf_property_t *scf_prop = scf_property_create(cfg->handles.main);
183 	scf_value_t *value = scf_value_create(cfg->handles.main);
184 
185 
186 	if (0 > scf_pg_get_property(cfg->handles.config_pg, name, scf_prop))
187 	/* this is OK: the property is just undefined */
188 		goto destruction;
189 
190 	if (0 > scf_property_get_value(scf_prop, value)) {
191 		idmapdlog(LOG_ERR,
192 		    "%s: scf_property_get_value(%s) failed: %s",
193 		    me, name, scf_strerror(scf_error()));
194 		rc = -1;
195 		goto destruction;
196 	}
197 
198 	if (!(*val = scf_value2string(value))) {
199 		rc = -1;
200 		idmapdlog(LOG_ERR,
201 		    "%s: scf_value2string(%s) failed: %s",
202 		    me, name, scf_strerror(scf_error()));
203 	}
204 
205 destruction:
206 	scf_value_destroy(value);
207 	scf_property_destroy(scf_prop);
208 
209 	if (rc < 0) {
210 		if (*val)
211 			free(*val);
212 		*val = NULL;
213 	}
214 
215 	return (rc);
216 }
217 
218 static int
219 set_val_astring(idmap_cfg_t *cfg, char *name, const char *val)
220 {
221 	int			rc = 0, i;
222 	scf_property_t		*scf_prop = NULL;
223 	scf_value_t		*value = NULL;
224 	scf_transaction_t	*tx = NULL;
225 	scf_transaction_entry_t	*ent = NULL;
226 
227 	if ((scf_prop = scf_property_create(cfg->handles.main)) == NULL ||
228 	    (value = scf_value_create(cfg->handles.main)) == NULL ||
229 	    (tx = scf_transaction_create(cfg->handles.main)) == NULL ||
230 	    (ent = scf_entry_create(cfg->handles.main)) == NULL) {
231 		idmapdlog(LOG_ERR, "%s: Unable to set property %s: %s",
232 		    me, name, scf_strerror(scf_error()));
233 		rc = -1;
234 		goto destruction;
235 	}
236 
237 	for (i = 0; i < MAX_TRIES && rc == 0; i++) {
238 		if (scf_transaction_start(tx, cfg->handles.config_pg) == -1) {
239 			idmapdlog(LOG_ERR,
240 			    "%s: scf_transaction_start(%s) failed: %s",
241 			    me, name, scf_strerror(scf_error()));
242 			rc = -1;
243 			goto destruction;
244 		}
245 
246 		rc = scf_transaction_property_new(tx, ent, name,
247 		    SCF_TYPE_ASTRING);
248 		if (rc == -1) {
249 			idmapdlog(LOG_ERR,
250 			    "%s: scf_transaction_property_new() failed: %s",
251 			    me, scf_strerror(scf_error()));
252 			goto destruction;
253 		}
254 
255 		if (scf_value_set_astring(value, val) == -1) {
256 			idmapdlog(LOG_ERR,
257 			    "%s: scf_value_set_astring() failed: %s",
258 			    me, scf_strerror(scf_error()));
259 			rc = -1;
260 			goto destruction;
261 		}
262 
263 		if (scf_entry_add_value(ent, value) == -1) {
264 			idmapdlog(LOG_ERR,
265 			    "%s: scf_entry_add_value() failed: %s",
266 			    me, scf_strerror(scf_error()));
267 			rc = -1;
268 			goto destruction;
269 		}
270 
271 		rc = scf_transaction_commit(tx);
272 		if (rc == 0 && i < MAX_TRIES - 1) {
273 			/*
274 			 * Property group set in scf_transaction_start()
275 			 * is not the most recent. Update pg, reset tx and
276 			 * retry tx.
277 			 */
278 			idmapdlog(LOG_WARNING,
279 			    "%s: scf_transaction_commit(%s) failed - Retry: %s",
280 			    me, name, scf_strerror(scf_error()));
281 			if (scf_pg_update(cfg->handles.config_pg) == -1) {
282 				idmapdlog(LOG_ERR,
283 				    "%s: scf_pg_update() failed: %s",
284 				    me, scf_strerror(scf_error()));
285 				rc = -1;
286 				goto destruction;
287 			}
288 			scf_transaction_reset(tx);
289 		}
290 	}
291 
292 	/* Log failure message if all retries failed */
293 	if (rc == 0) {
294 		idmapdlog(LOG_ERR,
295 		    "%s: scf_transaction_commit(%s) failed: %s",
296 		    me, name, scf_strerror(scf_error()));
297 		rc = -1;
298 	}
299 
300 destruction:
301 	scf_value_destroy(value);
302 	scf_entry_destroy(ent);
303 	scf_transaction_destroy(tx);
304 	scf_property_destroy(scf_prop);
305 	return (rc);
306 }
307 
308 int
309 idmap_cfg_load(idmap_cfg_t *cfg)
310 {
311 	int rc = 0;
312 
313 	cfg->pgcfg.list_size_limit = 0;
314 	cfg->pgcfg.mapping_domain = NULL;
315 	cfg->pgcfg.machine_sid = NULL;
316 	cfg->pgcfg.domain_controller = NULL;
317 	cfg->pgcfg.global_catalog = NULL;
318 
319 	if (0 > scf_pg_update(cfg->handles.config_pg)) {
320 		idmapdlog(LOG_ERR, "%s: scf_pg_update() failed: %s",
321 		    me, scf_strerror(scf_error()));
322 		return (-1);
323 	}
324 
325 	if (0 > scf_pg_update(cfg->handles.general_pg)) {
326 		idmapdlog(LOG_ERR, "%s: scf_pg_update() failed: %s",
327 		    me, scf_strerror(scf_error()));
328 		return (-1);
329 	}
330 
331 	rc = get_val_int(cfg, "list_size_limit",
332 	    &cfg->pgcfg.list_size_limit, SCF_TYPE_COUNT);
333 	if (rc != 0)
334 		return (-1);
335 
336 	rc = get_val_astring(cfg, "mapping_domain",
337 	    &cfg->pgcfg.mapping_domain);
338 	if (rc != 0)
339 		return (-1);
340 
341 	/*
342 	 * If there is no mapping_domain in idmap's smf config then
343 	 * set it to the joined domain.
344 	 * Till domain join is implemented, temporarily set it to
345 	 * the system domain for testing purposes.
346 	 */
347 	if (!cfg->pgcfg.mapping_domain) 	{
348 		char test[1];
349 		long dname_size = sysinfo(SI_SRPC_DOMAIN, test, 1);
350 		if (dname_size > 0) {
351 			cfg->pgcfg.mapping_domain =
352 			    (char *)malloc(dname_size * sizeof (char));
353 			dname_size = sysinfo(SI_SRPC_DOMAIN,
354 			    cfg->pgcfg.mapping_domain, dname_size);
355 		}
356 		if (dname_size <= 0) {
357 			idmapdlog(LOG_ERR,
358 			    "%s: unable to get name service domain", me);
359 			if (cfg->pgcfg.mapping_domain)
360 				free(cfg->pgcfg.mapping_domain);
361 			cfg->pgcfg.mapping_domain = NULL;
362 		}
363 	}
364 
365 	rc = get_val_astring(cfg, "machine_sid", &cfg->pgcfg.machine_sid);
366 	if (rc != 0)
367 		return (-1);
368 	if (cfg->pgcfg.machine_sid == NULL) {
369 		/* If machine_sid not configured, generate one */
370 		if (generate_machine_sid(&cfg->pgcfg.machine_sid) < 0)
371 			return (-1);
372 		rc = set_val_astring(cfg, "machine_sid",
373 		    cfg->pgcfg.machine_sid);
374 		if (rc < 0) {
375 			free(cfg->pgcfg.machine_sid);
376 			cfg->pgcfg.machine_sid = NULL;
377 			return (-1);
378 		}
379 	}
380 
381 	rc = get_val_astring(cfg, "global_catalog", &cfg->pgcfg.global_catalog);
382 	if (rc != 0)
383 		return (-1);
384 
385 	rc = get_val_astring(cfg, "domain_controller",
386 	    &cfg->pgcfg.domain_controller);
387 	if (rc != 0)
388 		return (-1);
389 
390 	return (rc);
391 }
392 
393 /*
394  * Initialize 'cfg'.
395  */
396 idmap_cfg_t *
397 idmap_cfg_init() {
398 
399 	/* First the smf repository handles: */
400 	idmap_cfg_t *cfg = calloc(1, sizeof (idmap_cfg_t));
401 	if (!cfg) {
402 		idmapdlog(LOG_ERR, "%s: Out of memory", me);
403 		return (NULL);
404 	}
405 
406 	if (!(cfg->handles.main = scf_handle_create(SCF_VERSION))) {
407 		idmapdlog(LOG_ERR, "%s: scf_handle_create() failed: %s",
408 		    me, scf_strerror(scf_error()));
409 		goto error;
410 	}
411 
412 	if (0 > scf_handle_bind(cfg->handles.main)) {
413 		idmapdlog(LOG_ERR, "%s: scf_handle_bind() failed: %s",
414 		    me, scf_strerror(scf_error()));
415 		goto error;
416 	}
417 
418 	if (!(cfg->handles.service = scf_service_create(cfg->handles.main)) ||
419 	    !(cfg->handles.instance = scf_instance_create(cfg->handles.main)) ||
420 	    !(cfg->handles.config_pg = scf_pg_create(cfg->handles.main)) ||
421 	    !(cfg->handles.general_pg = scf_pg_create(cfg->handles.main))) {
422 		idmapdlog(LOG_ERR, "%s: scf handle creation failed: %s",
423 		    me, scf_strerror(scf_error()));
424 		goto error;
425 	}
426 
427 	if (0 > scf_handle_decode_fmri(cfg->handles.main,
428 		FMRI_BASE "/:properties/" CONFIG_PG,
429 		NULL,				/* scope */
430 		cfg->handles.service,		/* service */
431 		cfg->handles.instance,		/* instance */
432 		cfg->handles.config_pg,		/* pg */
433 		NULL,				/* prop */
434 		SCF_DECODE_FMRI_EXACT)) {
435 		idmapdlog(LOG_ERR, "%s: scf_handle_decode_fmri() failed: %s",
436 		    me, scf_strerror(scf_error()));
437 		goto error;
438 
439 	}
440 
441 	if (0 > scf_service_get_pg(cfg->handles.service,
442 		GENERAL_PG, cfg->handles.general_pg)) {
443 		idmapdlog(LOG_ERR, "%s: scf_service_get_pg() failed: %s",
444 		    me, scf_strerror(scf_error()));
445 		goto error;
446 	}
447 
448 	return (cfg);
449 
450 error:
451 	(void) idmap_cfg_fini(cfg);
452 	return (NULL);
453 }
454 
455 /* ARGSUSED */
456 static void
457 idmap_pgcfg_fini(idmap_pg_config_t *pgcfg) {
458 	if (pgcfg->mapping_domain)
459 		free(pgcfg->mapping_domain);
460 	if (pgcfg->machine_sid)
461 		free(pgcfg->mapping_domain);
462 	if (pgcfg->global_catalog)
463 		free(pgcfg->global_catalog);
464 	if (pgcfg->domain_controller)
465 		free(pgcfg->domain_controller);
466 }
467 
468 int
469 idmap_cfg_fini(idmap_cfg_t *cfg)
470 {
471 	idmap_pgcfg_fini(&cfg->pgcfg);
472 
473 	scf_pg_destroy(cfg->handles.config_pg);
474 	scf_pg_destroy(cfg->handles.general_pg);
475 	scf_instance_destroy(cfg->handles.instance);
476 	scf_service_destroy(cfg->handles.service);
477 	scf_handle_destroy(cfg->handles.main);
478 	free(cfg);
479 
480 	return (0);
481 }
482