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