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 2006 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  *	Label library contract private interfaces.
30  *
31  *	Binary labels to String labels with dimming word lists.
32  *	Dimming word list titles.
33  *	Default user labels.
34  */
35 
36 #include <locale.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <strings.h>
40 
41 #include <sys/mman.h>
42 
43 #include <tsol/label.h>
44 
45 #include "clnt.h"
46 #include "labeld.h"
47 
48 /*
49  *	cvt memory:
50  *
51  * cvt:	char	*long_words[display_size];	Pointers to long words
52  *	char	*short_words[display_size];	Pointers to short words
53  * dim:	char	display[display_size];		Dim | Set
54  *
55  *	    strings associated with long and short words.
56  *
57  */
58 
59 /*
60  *	Sensitivity Label words.
61  */
62 
63 static	char *slcvt = NULL;
64 static	int   slcvtsize = 0;
65 static	char *sldim;
66 
67 static	char *slstring = NULL;
68 static	int   slstringsize = 0;
69 static	brange_t sbounds;
70 
71 /*
72  *	Clearance words.
73  */
74 
75 static	char *clrcvt = NULL;
76 static	int   clrcvtsize = 0;
77 static	char *clrdim;
78 
79 static	char *clrstring = NULL;
80 static	int   clrstringsize = 0;
81 static	brange_t cbounds;
82 
83 static
84 int
alloc_words(char ** words,const size_t size)85 alloc_words(char **words, const size_t size)
86 {
87 	if (*words == NULL) {
88 		if ((*words = malloc(size)) == NULL)
89 			return (0);
90 	} else {
91 		if ((*words = realloc(*words, size)) == NULL) {
92 			return (0);
93 		}
94 	}
95 	return (1);
96 }
97 
98 /*
99  *	build_strings - Build the static strings and dimming list for a
100  *			converted label.
101  *
102  *	Entry	new_string = Newly converted string.
103  *		new_words_size = Size of words associated with newly converted
104  *				 label.
105  *		number_of_words = Number of words associated with newly
106  *				  converted label.
107  *		full =	1, if static words lists to be updated.
108  *			0, if only string and dimming list to be updated.
109  *
110  *	Exit	static_string_size = Updated if needed.
111  *		static_string = Updated to new label string.
112  *		static_words_size = Updated if needed.
113  *		static_words = Updated to new words list, if needed.
114  *		static_dimming = Updated to new dimming state.
115  *		long_words = Updated to new long words pointers, if needed.
116  *		short_words = Updated to new short words pointers, if needed.
117  *
118  *
119  *	Returns	0, If unable to allocate memory.
120  *		1, If successful.
121  *
122  *	Calls	alloc_string, alloc_words, memcpy, strcpy, strlen.
123  */
124 
125 static
126 int
build_strings(int * static_string_size,char ** static_string,char * new_string,int * static_words_size,int new_words_size,char ** static_words,char ** static_dimming,int number_of_words,char * long_words,char * short_words,char * dimming_list,int full)127 build_strings(int *static_string_size, char **static_string, char *new_string,
128     int *static_words_size, int new_words_size, char **static_words,
129     char **static_dimming, int number_of_words, char *long_words,
130     char *short_words, char *dimming_list, int full)
131 {
132 	char	**l;
133 	char	**s;
134 	char	*w;
135 	char	*l_w = long_words;
136 	char	*s_w = short_words;
137 	int	i;
138 	int	len;
139 	int	newsize;
140 
141 	if (*static_string_size == 0) { /* Allocate string memory. */
142 		if ((*static_string_size = alloc_string(static_string,
143 		    *static_string_size, 'C')) == 0)
144 			/* can't get string memory for string */
145 			return (0);
146 	}
147 
148 again:
149 	if (*static_string_size < (int)strlen(new_string)+1) {
150 		/* need longer string */
151 		if ((newsize = alloc_string(static_string, *static_string_size,
152 		    'C')) == 0)
153 			/* can't get more string memory */
154 			return (0);
155 
156 		*static_string_size += newsize;
157 		goto again;
158 	}
159 	bcopy(new_string, *static_string, strlen(new_string) + 1);
160 
161 	if (full) {
162 		if (*static_words_size < new_words_size &&
163 		    !alloc_words(static_words, new_words_size)) {
164 			/* can't get more words memory */
165 			return (0);
166 		} else {
167 			*static_words_size = new_words_size;
168 		}
169 		/*LINTED*/
170 		l = (char **)*static_words;
171 		s = l + number_of_words;
172 		*static_dimming = (char *)(s + number_of_words);
173 		w = *static_dimming + number_of_words;
174 		for (i = 0; i < number_of_words; i++) {
175 			*l = w;
176 			(void) strcpy(w, l_w);
177 			w += (len = strlen(l_w) + 1);
178 			l_w += len;
179 			if (*s_w == '\000') {
180 				*s = NULL;
181 				s_w++;
182 			} else {
183 				*s = w;
184 				(void) strcpy(w, s_w);
185 				w += (len = strlen(s_w) + 1);
186 				s_w += len;
187 			}
188 
189 			l++;
190 			s++;
191 		}  /* for each word entry */
192 	}  /* if (full) */
193 
194 	bcopy(dimming_list, *static_dimming, number_of_words);
195 	return (1);
196 }  /* build_strings */
197 
198 #define	bsfcall callp->param.acall.cargs.bslcvt_arg
199 #define	bsfret callp->param.aret.rvals.bslcvt_ret
200 /*
201  *	bslcvtfull - Convert Sensitivity Label and initialize static
202  *			information.
203  *
204  *	Entry	label = Sensitivity Label to convert and get dimming list.
205  *			This label should lie within the bounds or the
206  *			results may not be meaningful.
207  *		bounds = Lower and upper bounds for words lists. Must be
208  *			dominated by clearance.
209  *		flags = VIEW_INTERNAL, don't promote/demote admin low/high.
210  *			VIEW_EXTERNAL, promote/demote admin low/high.
211  *
212  *	Exit	string = ASCII coded Sensitivity Label.
213  *		long_words = Array of pointers to visible long word names.
214  *		short_words = Array of pointers to visible short word names.
215  *		display = Array of indicators as to whether the word is present
216  *			  in the converted label (CVT_SET), and/or changeable
217  *			  (CVT_DIM).
218  *		first_compartment = Zero based index of first compartment.
219  *		display_size = Number of entries in the display/words lists.
220  *
221  *	Returns	-1, If unable to access label encodings database, or
222  *			invalid label.
223  *		 0, If unable to allocate static memory.
224  *		 1, If successful.
225  *
226  *	Calls	RPC - LABELS_BSLCONVERT, STTBLEVEL, SETBSLABEL, TCLNT,
227  *			build_strings, clnt_call, clnt_perror.
228  *
229  *	Uses	sbounds, slrcvt, slrcvtsize, slrdim, slrstring,
230  *			slrstringsize.
231  */
232 
233 int
bslcvtfull(const bslabel_t * label,const blrange_t * bounds,int flags,char ** string,char ** long_words[],char ** short_words[],char * display[],int * first_compartment,int * display_size)234 bslcvtfull(const bslabel_t *label, const blrange_t *bounds, int flags,
235     char **string, char **long_words[], char **short_words[], char *display[],
236     int *first_compartment, int *display_size)
237 {
238 	labeld_data_t	call;
239 	labeld_data_t	*callp = &call;
240 	size_t	bufsize = sizeof (labeld_data_t);
241 	size_t	datasize = CALL_SIZE(bslcvt_call_t, 0);
242 	int	new_words_size;
243 	int	rval;
244 
245 	call.callop = BSLCVT;
246 	bsfcall.label = *label;
247 	bsfcall.bounds.upper_bound = *bounds->upper_bound;
248 	bsfcall.bounds.lower_bound = *bounds->lower_bound;
249 	bsfcall.flags = LABELS_FULL_CONVERT;
250 	set_label_view(&bsfcall.flags, flags);
251 
252 	if ((rval = __call_labeld(&callp, &bufsize, &datasize)) == NOSERVER) {
253 #ifdef	DEBUG
254 		(void) fprintf(stderr, "No label server.\n");
255 #endif	/* DEBUG */
256 		return (-1);
257 	} else if (rval != SUCCESS) {
258 		return (-1);
259 	} else {
260 		if (callp->reterr != 0)
261 			return (-1);
262 	}
263 
264 	*first_compartment = bsfret.first_comp;
265 	*display_size = bsfret.d_len;
266 
267 	new_words_size = bsfret.l_len + bsfret.s_len + bsfret.d_len +
268 	    (2 * sizeof (char *)) * bsfret.d_len;
269 
270 	if (build_strings(&slstringsize, &slstring, &bsfret.buf[bsfret.string],
271 	    &slcvtsize, new_words_size, &slcvt, &sldim, bsfret.d_len,
272 	    &bsfret.buf[bsfret.lwords], &bsfret.buf[bsfret.swords],
273 	    &bsfret.buf[bsfret.dim], 1) != 1) {
274 		if (callp != &call)
275 			/* release return buffer */
276 			(void) munmap((void *)callp, bufsize);
277 		return (0);
278 	}
279 
280 	/* save for bslcvt call */
281 	sbounds.upper_bound = *bounds->upper_bound;
282 	sbounds.lower_bound = *bounds->lower_bound;
283 
284 	*string = slstring;
285 	*display = sldim;
286 	/*LINTED*/
287 	*long_words = (char **)slcvt;
288 	/*LINTED*/
289 	*short_words = (char **)(slcvt + *display_size * sizeof (char *));
290 	if (callp != &call)
291 		/* release return buffer */
292 		(void) munmap((void *)callp, bufsize);
293 	return (1);
294 }  /* bslcvtfull */
295 #undef	bsfcall
296 #undef	bsfret
297 
298 #define	bsccall callp->param.acall.cargs.bslcvt_arg
299 #define	bscret callp->param.aret.rvals.bslcvt_ret
300 /*
301  *	bslcvt - Convert Sensitivity Label and update dimming information.
302  *
303  *	Entry	label = Sensitivity Label to convert and get dimming list.
304  *			This label should lie within the bounds of the
305  *			corresponding bslcvtfull call or the results may
306  *			not be meaningful.
307  *		flags = VIEW_INTERNAL, don't promote/demote admin low/high.
308  *			VIEW_EXTERNAL, promote/demote admin low/high.
309  *
310  *	Exit	string = ASCII coded Sensitivity Label.
311  *		display = Array of indicators as to whether the word is present
312  *			  in the converted label (CVT_SET), and/or changeable
313  *			  (CVT_DIM).
314  *
315  *	Returns	-1, If unable to access label encodings database, or
316  *			invalid label.
317  *		 0, If unable to allocate static memory.
318  *		 1, If successful.
319  *
320  *	Calls	RPC - LABELS_BSLCONVERT, SETBLEVEL, SETBSLABEL, build_strings
321  *			clnt_call, clnt_perror.
322  *
323  *	Uses	sbounds, slrdim, slrstring.
324  */
325 
326 int
bslcvt(const bslabel_t * label,int flags,char ** string,char * display[])327 bslcvt(const bslabel_t *label, int flags, char **string, char *display[])
328 {
329 	labeld_data_t	call;
330 	labeld_data_t	*callp = &call;
331 	size_t	bufsize = sizeof (labeld_data_t);
332 	size_t	datasize = CALL_SIZE(bslcvt_call_t, 0);
333 	int	rval;
334 
335 	if (slcvt == NULL)
336 		return (-1);	/* conversion not initialized */
337 
338 	call.callop = BSLCVT;
339 	bsccall.label = *label;
340 	bsccall.bounds = sbounds;	/* save from last bslcvtfull() call */
341 	bsccall.flags = 0;
342 	set_label_view(&bsccall.flags, flags);
343 
344 	if ((rval = __call_labeld(&callp, &bufsize, &datasize)) == NOSERVER) {
345 #ifdef	DEBUG
346 		(void) fprintf(stderr, "No label server.\n");
347 #endif	/* DEBUG */
348 		return (-1);
349 	} else if (rval != SUCCESS) {
350 		return (-1);
351 	} else {
352 		if (callp->reterr != 0)
353 			return (-1);
354 	}
355 
356 	if (build_strings(&slstringsize, &slstring, &bscret.buf[bscret.string],
357 	    &slcvtsize, 0, &slcvt, &sldim, bscret.d_len,
358 	    &bscret.buf[bscret.lwords], &bscret.buf[bscret.swords],
359 	    &bscret.buf[bscret.dim], 0) != 1) {
360 		if (callp != &call)
361 			/* release return buffer */
362 			(void) munmap((void *)callp, bufsize);
363 		return (0);
364 	}
365 
366 	*string = slstring;
367 	*display = sldim;
368 	if (callp != &call)
369 		/* release return buffer */
370 		(void) munmap((void *)callp, bufsize);
371 	return (1);
372 }  /* bslcvt */
373 #undef	bsccall
374 #undef	bscret
375 
376 #define	bcfcall callp->param.acall.cargs.bclearcvt_arg
377 #define	bcfret callp->param.aret.rvals.bclearcvt_ret
378 /*
379  *	bclearcvtfull - Convert Clearance and initialize static information.
380  *
381  *	Entry	clearance = Clearance to convert and get dimming list.
382  *			    This clearance should lie within the bounds or
383  *			    the results may not be meaningful.
384  *		bounds = Lower and upper bounds for words lists. Must be
385  *			dominated by clearance.
386  *		flags = VIEW_INTERNAL, don't promote/demote admin low/high.
387  *			VIEW_EXTERNAL, promote/demote admin low/high.
388  *
389  *	Exit	string = ASCII coded Clearance.
390  *		long_words = Array of pointers to visible long word names.
391  *		short_words = Array of pointers to visible short word names.
392  *		display = Array of indicators as to whether the word is present
393  *			  in the converted label (CVT_SET), and/or changeable
394  *			  (CVT_DIM).
395  *		first_compartment = Zero based index of first compartment.
396  *		display_size = Number of entries in the display/words lists.
397  *
398  *	Returns	-1, If unable to access label encodings database, or
399  *			invalid label.
400  *		 0, If unable to allocate static memory.
401  *		 1, If successful.
402  *
403  *	Calls	RPC - LABELS_BCLEARCONVERT, SETBCLEAR, SETBLEVEL, TCLNT,
404  *			build_strings, clnt_call, clnt_perror.
405  *
406  *	Uses	cbounds, clrcvt, clrcvtsize, clrdim, clrstring,
407  *			clrstringsize.
408  */
409 
410 int
bclearcvtfull(const bclear_t * clearance,const blrange_t * bounds,int flags,char ** string,char ** long_words[],char ** short_words[],char * display[],int * first_compartment,int * display_size)411 bclearcvtfull(const bclear_t *clearance, const blrange_t *bounds,
412     int flags, char **string, char **long_words[], char **short_words[],
413     char *display[], int *first_compartment, int *display_size)
414 {
415 	labeld_data_t	call;
416 	labeld_data_t	*callp = &call;
417 	size_t	bufsize = sizeof (labeld_data_t);
418 	size_t	datasize = CALL_SIZE(bclearcvt_call_t, 0);
419 	int	new_words_size;
420 	int	rval;
421 
422 	call.callop = BCLEARCVT;
423 	bcfcall.clear = *clearance;
424 	bcfcall.bounds.upper_bound = *bounds->upper_bound;
425 	bcfcall.bounds.lower_bound = *bounds->lower_bound;
426 	bcfcall.flags = LABELS_FULL_CONVERT;
427 	set_label_view(&bcfcall.flags, flags);
428 
429 	if ((rval = __call_labeld(&callp, &bufsize, &datasize)) == NOSERVER) {
430 #ifdef	DEBUG
431 		(void) fprintf(stderr, "No label server.\n");
432 #endif	/* DEBUG */
433 		return (-1);
434 	} else if (rval != SUCCESS) {
435 		return (-1);
436 	} else {
437 		if (callp->reterr != 0)
438 			return (-1);
439 	}
440 
441 	*first_compartment = bcfret.first_comp;
442 	*display_size = bcfret.d_len;
443 
444 	new_words_size = bcfret.l_len + bcfret.s_len + bcfret.d_len +
445 	    (2 * sizeof (char *)) * bcfret.d_len;
446 
447 	if (build_strings(&clrstringsize, &clrstring,
448 	    &bcfret.buf[bcfret.string],
449 	    &clrcvtsize, new_words_size, &clrcvt,
450 	    &clrdim, bcfret.d_len,
451 	    &bcfret.buf[bcfret.lwords], &bcfret.buf[bcfret.swords],
452 	    &bcfret.buf[bcfret.dim], 1) != 1) {
453 		if (callp != &call)
454 			/* release return buffer */
455 			(void) munmap((void *)callp, bufsize);
456 		return (0);
457 	}
458 
459 	/* save for bclearcvt call */
460 	cbounds.upper_bound = *bounds->upper_bound;
461 	cbounds.lower_bound = *bounds->lower_bound;
462 
463 	*string = clrstring;
464 	*display = clrdim;
465 	/*LINTED*/
466 	*long_words = (char **)clrcvt;
467 	/*LINTED*/
468 	*short_words = (char **)(clrcvt + *display_size * sizeof (char *));
469 	if (callp != &call)
470 		/* release return buffer */
471 		(void) munmap((void *)callp, bufsize);
472 	return (1);
473 }  /* bclearcvtfull */
474 #undef	bcfcall
475 #undef	bcfret
476 
477 #define	bcccall callp->param.acall.cargs.bclearcvt_arg
478 #define	bccret callp->param.aret.rvals.bclearcvt_ret
479 /*
480  *	bclearcvt - Convert Clearance and update dimming inforamtion.
481  *
482  *	Entry	clearance = Clearance to convert and get dimming list.
483  *			    This clearance should lie within the bounds of the
484  *			    corresponding bclearcvtfull call or the results may
485  *			    not be meaningful.
486  *		flags = VIEW_INTERNAL, don't promote/demote admin low/high.
487  *			VIEW_EXTERNAL, promote/demote admin low/high.
488  *
489  *	Exit	string = ASCII coded Clearance.
490  *		display = Array of indicators as to whether the word is present
491  *			  in the converted label (CVT_SET), and/or changeable
492  *			  (CVT_DIM).
493  *
494  *	Returns	-1, If unable to access label encodings database, or
495  *			invalid label.
496  *		 0, If unable to allocate static memory.
497  *		 1, If successful.
498  *
499  *	Calls	RPC - LABELS_BCLEARCONVERT, SETBCLEAR, SETBLEVEL, build_strings,
500  *			clnt_call, clnt_perror.
501  *
502  *	Uses	cbounds, clrdim, clrstring.
503  */
504 
505 int
bclearcvt(const bclear_t * clearance,int flags,char ** string,char * display[])506 bclearcvt(const bclear_t *clearance, int flags, char **string,
507     char *display[])
508 {
509 	labeld_data_t	call;
510 	labeld_data_t	*callp = &call;
511 	size_t	bufsize = sizeof (labeld_data_t);
512 	size_t	datasize = CALL_SIZE(bclearcvt_call_t, 0);
513 	int	rval;
514 
515 	if (clrcvt == NULL)
516 		return (-1);	/* conversion not initialized */
517 
518 	call.callop = BCLEARCVT;
519 	bcccall.clear = *clearance;
520 	bcccall.bounds = cbounds;	/* save from last bslcvtfull() call */
521 	bcccall.flags = 0;
522 	set_label_view(&bcccall.flags, flags);
523 
524 	if ((rval = __call_labeld(&callp, &bufsize, &datasize)) == NOSERVER) {
525 #ifdef	DEBUG
526 		(void) fprintf(stderr, "No label server.\n");
527 #endif	/* DEBUG */
528 		return (-1);
529 	} else if (rval != SUCCESS) {
530 		return (-1);
531 	} else {
532 		if (callp->reterr != 0)
533 			return (-1);
534 	}
535 
536 	if (build_strings(&clrstringsize, &clrstring,
537 	    &bccret.buf[bccret.string],
538 	    &clrcvtsize, 0, &clrcvt, &clrdim, bccret.d_len,
539 	    &bccret.buf[bccret.lwords], &bccret.buf[bccret.swords],
540 	    &bccret.buf[bccret.dim], 0) != 1) {
541 		if (callp != &call)
542 			/* release return buffer */
543 			(void) munmap((void *)callp, bufsize);
544 		return (0);
545 	}
546 
547 	*string = clrstring;
548 	*display = clrdim;
549 	if (callp != &call)
550 		/* release return buffer */
551 		(void) munmap((void *)callp, bufsize);
552 	return (1);
553 }  /* bclearcvt */
554 #undef	bcccall
555 #undef	bccret
556 
557 #define	lfret callp->param.aret.rvals.fields_ret
558 /*
559  *	labelfields - Return names for the label fields.
560  *
561  *	Entry	None
562  *
563  *	Exit	fields = Updated.
564  *
565  *	Returns	-1, If unable to access label encodings file, or
566  *			labels server failure.
567  *		 0, If unable to allocate memory.
568  *		 1, If successful.
569  *
570  *	Calls __call_labeld(LABELFIELDS).
571  */
572 
573 int
labelfields(struct name_fields * fields)574 labelfields(struct name_fields *fields)
575 {
576 	labeld_data_t	call;
577 	labeld_data_t	*callp = &call;
578 	size_t	bufsize = sizeof (labeld_data_t);
579 	size_t	datasize = CALL_SIZE(fields_call_t, 0);
580 	int	rval;
581 
582 	call.callop = LABELFIELDS;
583 
584 	if ((rval = __call_labeld(&callp, &bufsize, &datasize)) != SUCCESS) {
585 
586 		if (callp != &call)
587 			/* release return buffer */
588 			(void) munmap((void *)callp, bufsize);
589 		return (-1);
590 	}
591 
592 	/* unpack results */
593 
594 	if ((fields->class_name = strdup(&lfret.buf[lfret.classi])) == NULL) {
595 		if (callp != &call)
596 			/* release return buffer */
597 			(void) munmap((void *)callp, bufsize);
598 		return (0);
599 	}
600 	if ((fields->comps_name = strdup(&lfret.buf[lfret.compsi])) == NULL) {
601 		free(fields->class_name);
602 		if (callp != &call)
603 			/* release return buffer */
604 			(void) munmap((void *)callp, bufsize);
605 		return (0);
606 	}
607 	if ((fields->marks_name = strdup(&lfret.buf[lfret.marksi])) == NULL) {
608 		free(fields->class_name);
609 		free(fields->comps_name);
610 		if (callp != &call)
611 			/* release return buffer */
612 			(void) munmap((void *)callp, bufsize);
613 		return (0);
614 	}
615 
616 	if (callp != &call)
617 		/* release return buffer */
618 		(void) munmap((void *)callp, bufsize);
619 	return (rval);
620 }  /* labelfields */
621 #undef	lfret
622 
623 #define	udret callp->param.aret.rvals.udefs_ret
624 /*
625  *	userdefs - Get default user Sensitivity Label and/or Clearance.
626  *
627  *	Entry   None.
628  *
629  *	Exit	sl = default user Sensitivity Label.
630  *		clear = default user Clearance.
631  *
632  *	Returns -1, If unable to access label encodings file, or
633  *			labels server failure.
634  *		1, If successful.
635  *
636  *	Calls	__call_labeld(UDEFS).
637  */
638 
639 int
userdefs(bslabel_t * sl,bclear_t * clear)640 userdefs(bslabel_t *sl, bclear_t *clear)
641 {
642 	labeld_data_t	call;
643 	labeld_data_t	*callp = &call;
644 	size_t	bufsize = sizeof (labeld_data_t);
645 	size_t	datasize = CALL_SIZE(udefs_call_t, 0);
646 	int	rval;
647 
648 	call.callop = UDEFS;
649 
650 	if ((rval = __call_labeld(&callp, &bufsize, &datasize)) != SUCCESS) {
651 		/* process error */
652 
653 		return (-1);
654 	}
655 
656 	if (sl != NULL)
657 		*sl = udret.sl;
658 	if (clear != NULL)
659 		*clear = udret.clear;
660 	return (rval);
661 }  /* userdefs */
662 #undef	udret
663