1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * prof_init.c --- routines that manipulate the user-visible profile_t
10  * 	object.
11  */
12 
13 #include "prof_int.h"
14 
15 #include <stdio.h>
16 #include <string.h>
17 #ifdef HAVE_STDLIB_H
18 #include <stdlib.h>
19 #endif
20 #include <errno.h>
21 
22 /* Find a 4-byte integer type */
23 #if	(SIZEOF_SHORT == 4)
24 typedef short	prof_int32;
25 #elif	(SIZEOF_INT == 4)
26 typedef int	prof_int32;
27 #elif	(SIZEOF_LONG == 4)
28 typedef long	prof_int32;
29 #else	/* SIZEOF_LONG == 4 */
30 error(do not have a 4-byte integer type)
31 #endif	/* SIZEOF_LONG == 4 */
32 
33 errcode_t KRB5_CALLCONV
34 profile_init(const_profile_filespec_t *files, profile_t *ret_profile)
35 {
36 	const_profile_filespec_t *fs;
37 	profile_t profile;
38 	prf_file_t  new_file, last = 0;
39 	errcode_t retval = 0;
40 
41 	profile = malloc(sizeof(struct _profile_t));
42 	if (!profile)
43 		return ENOMEM;
44 	memset(profile, 0, sizeof(struct _profile_t));
45 	profile->magic = PROF_MAGIC_PROFILE;
46 
47         /* if the filenames list is not specified return an empty profile */
48         if ( files ) {
49 	    for (fs = files; !PROFILE_LAST_FILESPEC(*fs); fs++) {
50 		retval = profile_open_file(*fs, &new_file);
51 		/* if this file is missing, skip to the next */
52 		if (retval == ENOENT || retval == EACCES) {
53 			continue;
54 		}
55 		if (retval) {
56 			profile_release(profile);
57 			return retval;
58 		}
59 		if (last)
60 			last->next = new_file;
61 		else
62 			profile->first_file = new_file;
63 		last = new_file;
64 	    }
65 	    /*
66 	     * If last is still null after the loop, then all the files were
67 	     * missing, so return the appropriate error.
68 	     */
69 	    if (!last) {
70 		profile_release(profile);
71 		return ENOENT;
72 	    }
73 	}
74 
75         *ret_profile = profile;
76         return 0;
77 }
78 
79 errcode_t KRB5_CALLCONV
80 profile_init_path(const_profile_filespec_list_t filepath,
81 		  profile_t *ret_profile)
82 {
83 	int n_entries, i;
84 	unsigned int ent_len;
85 	const char *s, *t;
86 	profile_filespec_t *filenames;
87 	errcode_t retval;
88 
89 	/* count the distinct filename components */
90 	for(s = filepath, n_entries = 1; *s; s++) {
91 		if (*s == ':')
92 			n_entries++;
93 	}
94 
95 	/* the array is NULL terminated */
96 	filenames = (profile_filespec_t*) malloc((n_entries+1) * sizeof(char*));
97 	if (filenames == 0)
98 		return ENOMEM;
99 
100 	/* measure, copy, and skip each one */
101 	for(s = filepath, i=0; ((t = strchr(s, ':')) != NULL) ||
102 			((t=s+strlen(s)) != NULL); s=t+1, i++) {
103 		ent_len = t-s;
104 		filenames[i] = (char*) malloc(ent_len + 1);
105 		if (filenames[i] == 0) {
106 			/* if malloc fails, free the ones that worked */
107 			while(--i >= 0) free(filenames[i]);
108                         free(filenames);
109 			return ENOMEM;
110 		}
111 		strncpy(filenames[i], s, ent_len);
112 		filenames[i][ent_len] = 0;
113 		if (*t == 0) {
114 			i++;
115 			break;
116 		}
117 	}
118 	/* cap the array */
119 	filenames[i] = 0;
120 
121 	retval = profile_init((const_profile_filespec_t *) filenames,
122 			      ret_profile);
123 
124 	/* count back down and free the entries */
125 	while(--i >= 0) free(filenames[i]);
126 	free(filenames);
127 
128 	return retval;
129 }
130 
131 errcode_t KRB5_CALLCONV
132 profile_is_writable(profile_t profile, int *writable)
133 {
134 	if (!profile || profile->magic != PROF_MAGIC_PROFILE)
135 		return PROF_MAGIC_PROFILE;
136 
137 	if (!writable)
138 		return EINVAL;
139 
140 	if (profile->first_file)
141 		*writable = (profile->first_file->data->flags & PROFILE_FILE_RW);
142 
143 	return 0;
144 }
145 
146 errcode_t KRB5_CALLCONV
147 profile_is_modified(profile_t profile, int *modified)
148 {
149 	if (!profile || profile->magic != PROF_MAGIC_PROFILE)
150 		return PROF_MAGIC_PROFILE;
151 
152 	if (!modified)
153 		return EINVAL;
154 
155 	if (profile->first_file)
156 		*modified = (profile->first_file->data->flags & PROFILE_FILE_DIRTY);
157 
158 	return 0;
159 }
160 
161 errcode_t KRB5_CALLCONV
162 profile_flush(profile_t profile)
163 {
164 	if (!profile || profile->magic != PROF_MAGIC_PROFILE)
165 		return PROF_MAGIC_PROFILE;
166 
167 	if (profile->first_file)
168 		return profile_flush_file(profile->first_file);
169 
170 	return 0;
171 }
172 
173 errcode_t KRB5_CALLCONV
174 profile_flush_to_file(profile_t profile, const_profile_filespec_t outfile)
175 {
176 	if (!profile || profile->magic != PROF_MAGIC_PROFILE)
177 		return PROF_MAGIC_PROFILE;
178 
179 	if (profile->first_file)
180 		return profile_flush_file_to_file(profile->first_file,
181 						  outfile);
182 
183 	return 0;
184 }
185 
186 errcode_t KRB5_CALLCONV
187 profile_flush_to_buffer(profile_t profile, char **buf)
188 {
189     return profile_flush_file_data_to_buffer(profile->first_file->data, buf);
190 }
191 
192 void KRB5_CALLCONV
193 profile_free_buffer(profile_t profile, char *buf)
194 {
195     free(buf);
196 }
197 
198 void KRB5_CALLCONV
199 profile_abandon(profile_t profile)
200 {
201 	prf_file_t	p, next;
202 
203 	if (!profile || profile->magic != PROF_MAGIC_PROFILE)
204 		return;
205 
206 	for (p = profile->first_file; p; p = next) {
207 		next = p->next;
208 		profile_free_file(p);
209 	}
210 	profile->magic = 0;
211 	free(profile);
212 }
213 
214 void KRB5_CALLCONV
215 profile_release(profile_t profile)
216 {
217 	prf_file_t	p, next;
218 
219 	if (!profile || profile->magic != PROF_MAGIC_PROFILE)
220 		return;
221 
222 	for (p = profile->first_file; p; p = next) {
223 		next = p->next;
224 		profile_close_file(p);
225 	}
226 	profile->magic = 0;
227 	free(profile);
228 }
229 
230 /*
231  * Here begins the profile serialization functions.
232  */
233 /*ARGSUSED*/
234 errcode_t profile_ser_size(const char *unused, profile_t profile,
235 			   size_t *sizep)
236 {
237     size_t	required;
238     prf_file_t	pfp;
239 
240     required = 3*sizeof(prof_int32);
241     for (pfp = profile->first_file; pfp; pfp = pfp->next) {
242 	required += sizeof(prof_int32);
243 	required += strlen(pfp->data->filespec);
244     }
245     *sizep += required;
246     return 0;
247 }
248 
249 static void pack_int32(prof_int32 oval, unsigned char **bufpp, size_t *remainp)
250 {
251     (*bufpp)[0] = (unsigned char) ((oval >> 24) & 0xff);
252     (*bufpp)[1] = (unsigned char) ((oval >> 16) & 0xff);
253     (*bufpp)[2] = (unsigned char) ((oval >> 8) & 0xff);
254     (*bufpp)[3] = (unsigned char) (oval & 0xff);
255     *bufpp += sizeof(prof_int32);
256     *remainp -= sizeof(prof_int32);
257 }
258 
259 errcode_t profile_ser_externalize(const char *unused, profile_t profile,
260 				  unsigned char **bufpp, size_t *remainp)
261 {
262     errcode_t		retval;
263     size_t		required;
264     unsigned char	*bp;
265     size_t		remain;
266     prf_file_t		pfp;
267     prof_int32		fcount, slen;
268 
269     required = 0;
270     bp = *bufpp;
271     remain = *remainp;
272     retval = EINVAL;
273     if (profile) {
274 	retval = ENOMEM;
275 	(void) profile_ser_size(unused, profile, &required);
276 	if (required <= remain) {
277 	    fcount = 0;
278 	    for (pfp = profile->first_file; pfp; pfp = pfp->next)
279 		fcount++;
280 	    pack_int32((prof_int32)PROF_MAGIC_PROFILE, &bp, &remain);
281 	    pack_int32(fcount, &bp, &remain);
282 	    for (pfp = profile->first_file; pfp; pfp = pfp->next) {
283 		slen = (prof_int32) strlen(pfp->data->filespec);
284 		pack_int32(slen, &bp, &remain);
285 		if (slen) {
286 		    memcpy(bp, pfp->data->filespec, (size_t) slen);
287 		    bp += slen;
288 		    remain -= (size_t) slen;
289 		}
290 	    }
291 	    pack_int32((prof_int32)PROF_MAGIC_PROFILE, &bp, &remain);
292 	    retval = 0;
293 	    *bufpp = bp;
294 	    *remainp = remain;
295 	}
296     }
297     return(retval);
298 }
299 
300 static int unpack_int32(prof_int32 *intp, unsigned char **bufpp,
301 			size_t *remainp)
302 {
303     if (*remainp >= sizeof(prof_int32)) {
304 	*intp = (((prof_int32) (*bufpp)[0] << 24) |
305 		 ((prof_int32) (*bufpp)[1] << 16) |
306 		 ((prof_int32) (*bufpp)[2] << 8) |
307 		 ((prof_int32) (*bufpp)[3]));
308 	*bufpp += sizeof(prof_int32);
309 	*remainp -= sizeof(prof_int32);
310 	return 0;
311     }
312     else
313 	return 1;
314 }
315 
316 /*ARGSUSED*/
317 errcode_t profile_ser_internalize(const char *unused, profile_t *profilep,
318 				  unsigned char **bufpp, size_t *remainp)
319 {
320 	errcode_t		retval;
321 	unsigned char	*bp;
322 	size_t		remain;
323 	int			i;
324 	prof_int32		fcount, tmp;
325 	profile_filespec_t		*flist = 0;
326 
327 	bp = *bufpp;
328 	remain = *remainp;
329 
330 	if (remain >= 12)
331 		(void) unpack_int32(&tmp, &bp, &remain);
332 	else
333 		tmp = 0;
334 
335 	if (tmp != PROF_MAGIC_PROFILE) {
336 		retval = EINVAL;
337 		goto cleanup;
338 	}
339 
340 	(void) unpack_int32(&fcount, &bp, &remain);
341 	retval = ENOMEM;
342 
343 	flist = (profile_filespec_t *) malloc(sizeof(profile_filespec_t) * (fcount + 1));
344 	if (!flist)
345 		goto cleanup;
346 
347 	memset(flist, 0, sizeof(char *) * (fcount+1));
348 	for (i=0; i<fcount; i++) {
349 		if (!unpack_int32(&tmp, &bp, &remain)) {
350 			flist[i] = (char *) malloc((size_t) (tmp+1));
351 			if (!flist[i])
352 				goto cleanup;
353 			memcpy(flist[i], bp, (size_t) tmp);
354 			flist[i][tmp] = '\0';
355 			bp += tmp;
356 			remain -= (size_t) tmp;
357 		}
358 	}
359 
360 	if (unpack_int32(&tmp, &bp, &remain) ||
361 	    (tmp != PROF_MAGIC_PROFILE)) {
362 		retval = EINVAL;
363 		goto cleanup;
364 	}
365 
366 	if ((retval = profile_init((const_profile_filespec_t *) flist,
367 				   profilep)))
368 		goto cleanup;
369 
370 	*bufpp = bp;
371 	*remainp = remain;
372 
373 cleanup:
374 	if (flist) {
375 		for (i=0; i<fcount; i++) {
376 			if (flist[i])
377 				free(flist[i]);
378 		}
379 		free(flist);
380 	}
381 	return(retval);
382 }
383 
384 
385 errcode_t
386 profile_get_options_boolean(profile, section, options)
387     profile_t		profile;
388     char **		section;
389     profile_options_boolean *options;
390 {
391     char ** actual_section;
392     char * value = NULL;
393     errcode_t retval = 0;
394     int i, max_i;
395 
396     for (max_i = 0; section[max_i]; max_i++);
397     if (actual_section = (char **)malloc((max_i + 2) * sizeof(char *))) {
398         for (actual_section[max_i + 1] = NULL, i = 0; section[i]; i++)
399 	    actual_section[i] = section[i];
400 
401 	for (i = 0; options[i].name; i++) {
402 	    if (options[i].found) continue;
403 	    actual_section[max_i] = options[i].name;
404 	    retval = profile_get_value(profile, (const char **) actual_section,
405                                        (const char **)&value);
406 	    if (retval && (retval != PROF_NO_RELATION) &&
407 	       (retval != PROF_NO_SECTION)) {
408 		free(actual_section);
409 		return(retval);
410 	    }
411 	    if ((retval == 0) && value) {
412 		/*
413 		 * Any string other than true will turn off the
414 		 *option
415 		 */
416 		if (strncmp(value,"true",4) == 0)
417 		    *(options[i].value) = 1;
418 		else
419 		    *(options[i].value) = 0;
420 		options[i].found = 1;
421 
422 		}
423 	}
424 	free(actual_section);
425     } else {
426 	retval = ENOMEM;
427     }
428     return(retval);
429 }
430 
431 errcode_t
432 profile_get_options_string(profile, section, options)
433     profile_t		profile;
434     char **		section;
435     profile_option_strings *options;
436 {
437     char ** actual_section;
438     char * value = NULL;
439     errcode_t retval = 0;
440     int i, max_i;
441 
442     for (max_i = 0; section[max_i]; max_i++);
443     if (actual_section = (char **)malloc((max_i + 2) * sizeof(char *))) {
444         for (actual_section[max_i + 1] = NULL, i = 0; section[i]; i++)
445 	    actual_section[i] = section[i];
446 
447 	for (i = 0; options[i].name; i++) {
448 	    if (options[i].found) continue;
449 	    actual_section[max_i] = options[i].name;
450 	    retval = profile_get_value(profile, (const char **) actual_section,
451                                        (const char **)&value);
452 	    if (retval && (retval != PROF_NO_RELATION) &&
453 	       (retval != PROF_NO_SECTION)) {
454 		free(actual_section);
455 		return(retval);
456 	    }
457 	    if ((retval == 0) && value) {
458 		*options[i].value = malloc(strlen(value)+1);
459 		if (*options[i].value == 0)
460 			retval = ENOMEM;
461 		strcpy(*options[i].value, value);
462 	        options[i].found = 1;
463     	    } else
464 		*options[i].value = 0;
465 	}
466 	free(actual_section);
467     } else {
468 	retval = ENOMEM;
469     }
470     return(retval);
471 }
472