145916cd2Sjpk /* 245916cd2Sjpk * CDDL HEADER START 345916cd2Sjpk * 445916cd2Sjpk * The contents of this file are subject to the terms of the 545916cd2Sjpk * Common Development and Distribution License (the "License"). 645916cd2Sjpk * You may not use this file except in compliance with the License. 745916cd2Sjpk * 845916cd2Sjpk * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 945916cd2Sjpk * or http://www.opensolaris.org/os/licensing. 1045916cd2Sjpk * See the License for the specific language governing permissions 1145916cd2Sjpk * and limitations under the License. 1245916cd2Sjpk * 1345916cd2Sjpk * When distributing Covered Code, include this CDDL HEADER in each 1445916cd2Sjpk * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1545916cd2Sjpk * If applicable, add the following below this CDDL HEADER, with the 1645916cd2Sjpk * fields enclosed by brackets "[]" replaced with your own identifying 1745916cd2Sjpk * information: Portions Copyright [yyyy] [name of copyright owner] 1845916cd2Sjpk * 1945916cd2Sjpk * CDDL HEADER END 2045916cd2Sjpk */ 2145916cd2Sjpk /* 22*42096647STony Nguyen * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2345916cd2Sjpk * Use is subject to license terms. 2445916cd2Sjpk */ 2545916cd2Sjpk 2645916cd2Sjpk 2745916cd2Sjpk /* 2845916cd2Sjpk * Miscellaneous user interfaces to trusted label functions. 2945916cd2Sjpk * 3045916cd2Sjpk */ 3145916cd2Sjpk 3245916cd2Sjpk 3345916cd2Sjpk #include <ctype.h> 3445916cd2Sjpk #include <stdlib.h> 3545916cd2Sjpk #include <strings.h> 3645916cd2Sjpk 3745916cd2Sjpk #include <sys/mman.h> 3845916cd2Sjpk 3945916cd2Sjpk #include <tsol/label.h> 4045916cd2Sjpk 4145916cd2Sjpk #include "labeld.h" 4245916cd2Sjpk #include "clnt.h" 4345916cd2Sjpk #include <sys/tsol/label_macro.h> 4445916cd2Sjpk #include <secdb.h> 4545916cd2Sjpk #include <user_attr.h> 4645916cd2Sjpk 4745916cd2Sjpk static bslabel_t slow, shigh; /* static Admin Low and High SLs */ 4845916cd2Sjpk static bclear_t clow, chigh; /* static Admin Low and High CLRs */ 4945916cd2Sjpk 5045916cd2Sjpk static char color[MAXCOLOR]; 5145916cd2Sjpk 5245916cd2Sjpk 5345916cd2Sjpk #define incall callp->param.acall.cargs.inset_arg 5445916cd2Sjpk #define inret callp->param.aret.rvals.inset_ret 5545916cd2Sjpk /* 5645916cd2Sjpk * blinset - Check in a label set. 5745916cd2Sjpk * 5845916cd2Sjpk * Entry label = Sensitivity Label to check. 5945916cd2Sjpk * id = Label set identifier of set to check. 6045916cd2Sjpk * 6145916cd2Sjpk * Exit None. 6245916cd2Sjpk * 6345916cd2Sjpk * Returns -1, If label set unavailable, or server failure. 6445916cd2Sjpk * 0, If label not in label set. 6545916cd2Sjpk * 1, If label is in the label set. 6645916cd2Sjpk * 6745916cd2Sjpk * Calls __call_labeld(BLINSET), BLTYPE, BSLLOW, BSLHIGH. 6845916cd2Sjpk * 6945916cd2Sjpk * Uses slow, shigh. 7045916cd2Sjpk */ 7145916cd2Sjpk 7245916cd2Sjpk int 7345916cd2Sjpk blinset(const bslabel_t *label, const set_id *id) 7445916cd2Sjpk { 7545916cd2Sjpk if (id->type == SYSTEM_ACCREDITATION_RANGE) { 7645916cd2Sjpk if (!BLTYPE(&slow, SUN_SL_ID)) { 7745916cd2Sjpk /* initialize static labels. */ 7845916cd2Sjpk 7945916cd2Sjpk BSLLOW(&slow); 8045916cd2Sjpk BSLHIGH(&shigh); 8145916cd2Sjpk } 8245916cd2Sjpk 8345916cd2Sjpk if (BLTYPE(label, SUN_SL_ID) && 8445916cd2Sjpk (BLEQUAL(label, &slow) || BLEQUAL(label, &shigh))) 8545916cd2Sjpk 8645916cd2Sjpk return (1); 8745916cd2Sjpk } 8845916cd2Sjpk if (id->type == USER_ACCREDITATION_RANGE || 8945916cd2Sjpk id->type == SYSTEM_ACCREDITATION_RANGE) { 9045916cd2Sjpk labeld_data_t call; 9145916cd2Sjpk labeld_data_t *callp = &call; 9245916cd2Sjpk size_t bufsize = sizeof (labeld_data_t); 9345916cd2Sjpk size_t datasize = CALL_SIZE(inset_call_t, 0); 9445916cd2Sjpk 9545916cd2Sjpk call.callop = BLINSET; 9645916cd2Sjpk incall.label = *label; 9745916cd2Sjpk incall.type = id->type; 9845916cd2Sjpk 9945916cd2Sjpk if (__call_labeld(&callp, &bufsize, &datasize) != SUCCESS) { 10045916cd2Sjpk /* process error */ 10145916cd2Sjpk 10245916cd2Sjpk return (-1); 10345916cd2Sjpk } 10445916cd2Sjpk return (inret.inset); 10545916cd2Sjpk } else { 10645916cd2Sjpk /* 10745916cd2Sjpk * Only System and User Accreditation Ranges presently 10845916cd2Sjpk * implemented. 10945916cd2Sjpk */ 11045916cd2Sjpk return (-1); 11145916cd2Sjpk } 11245916cd2Sjpk } 11345916cd2Sjpk #undef incall 11445916cd2Sjpk #undef inret 11545916cd2Sjpk 11645916cd2Sjpk #define slvcall callp->param.acall.cargs.slvalid_arg 11745916cd2Sjpk #define slvret callp->param.aret.rvals.slvalid_ret 11845916cd2Sjpk /* 11945916cd2Sjpk * bslvalid - Check Sensitivity Label for validity. 12045916cd2Sjpk * 12145916cd2Sjpk * Entry label = Sensitivity Label to check. 12245916cd2Sjpk * 12345916cd2Sjpk * Exit None. 12445916cd2Sjpk * 12545916cd2Sjpk * Returns -1, If unable to access label encodings file, or server failure. 12645916cd2Sjpk * 0, If label not valid. 12745916cd2Sjpk * 1, If label is valid. 12845916cd2Sjpk * 12945916cd2Sjpk * Calls __call_labeld(BSLVALID), BLTYPE, BSLLOW, BSLHIGH. 13045916cd2Sjpk * 13145916cd2Sjpk * Uses slow, shigh. 13245916cd2Sjpk * 13345916cd2Sjpk */ 13445916cd2Sjpk 13545916cd2Sjpk int 13645916cd2Sjpk bslvalid(const bslabel_t *label) 13745916cd2Sjpk { 13845916cd2Sjpk labeld_data_t call; 13945916cd2Sjpk labeld_data_t *callp = &call; 14045916cd2Sjpk size_t bufsize = sizeof (labeld_data_t); 14145916cd2Sjpk size_t datasize = CALL_SIZE(slvalid_call_t, 0); 14245916cd2Sjpk 14345916cd2Sjpk if (!BLTYPE(&slow, SUN_SL_ID)) { 14445916cd2Sjpk /* initialize static labels. */ 14545916cd2Sjpk 14645916cd2Sjpk BSLLOW(&slow); 14745916cd2Sjpk BSLHIGH(&shigh); 14845916cd2Sjpk } 14945916cd2Sjpk 15045916cd2Sjpk if (BLTYPE(label, SUN_SL_ID) && 15145916cd2Sjpk (BLEQUAL(label, &slow) || BLEQUAL(label, &shigh))) { 15245916cd2Sjpk 15345916cd2Sjpk return (1); 15445916cd2Sjpk } 15545916cd2Sjpk 15645916cd2Sjpk call.callop = BSLVALID; 15745916cd2Sjpk slvcall.label = *label; 15845916cd2Sjpk 15945916cd2Sjpk if (__call_labeld(&callp, &bufsize, &datasize) != SUCCESS) { 16045916cd2Sjpk /* process error */ 16145916cd2Sjpk 16245916cd2Sjpk return (-1); 16345916cd2Sjpk } 16445916cd2Sjpk return (slvret.valid); 16545916cd2Sjpk } 16645916cd2Sjpk #undef slvcall 16745916cd2Sjpk #undef slvret 16845916cd2Sjpk 16945916cd2Sjpk #define clrvcall callp->param.acall.cargs.clrvalid_arg 17045916cd2Sjpk #define clrvret callp->param.aret.rvals.clrvalid_ret 17145916cd2Sjpk /* 17245916cd2Sjpk * bclearvalid - Check Clearance for validity. 17345916cd2Sjpk * 17445916cd2Sjpk * Entry clearance = Clearance to check. 17545916cd2Sjpk * 17645916cd2Sjpk * Exit None. 17745916cd2Sjpk * 17845916cd2Sjpk * Returns -1, If unable to access label encodings file, or server failure. 17945916cd2Sjpk * 0, If label not valid. 18045916cd2Sjpk * 1, If label is valid. 18145916cd2Sjpk * 18245916cd2Sjpk * Calls __call_labeld(BCLEARVALID), BLTYPE, BCLEARLOW, BCLEARHIGH. 18345916cd2Sjpk * 18445916cd2Sjpk * Uses clow, chigh. 18545916cd2Sjpk * 18645916cd2Sjpk */ 18745916cd2Sjpk 18845916cd2Sjpk int 18945916cd2Sjpk bclearvalid(const bclear_t *clearance) 19045916cd2Sjpk { 19145916cd2Sjpk labeld_data_t call; 19245916cd2Sjpk labeld_data_t *callp = &call; 19345916cd2Sjpk size_t bufsize = sizeof (labeld_data_t); 19445916cd2Sjpk size_t datasize = CALL_SIZE(clrvalid_call_t, 0); 19545916cd2Sjpk 19645916cd2Sjpk if (!BLTYPE(&clow, SUN_CLR_ID)) { 19745916cd2Sjpk /* initialize static labels. */ 19845916cd2Sjpk 19945916cd2Sjpk BCLEARLOW(&clow); 20045916cd2Sjpk BCLEARHIGH(&chigh); 20145916cd2Sjpk } 20245916cd2Sjpk 20345916cd2Sjpk if (BLTYPE(clearance, SUN_CLR_ID) && 20445916cd2Sjpk (BLEQUAL(clearance, &clow) || BLEQUAL(clearance, &chigh))) { 20545916cd2Sjpk 20645916cd2Sjpk return (1); 20745916cd2Sjpk } 20845916cd2Sjpk 20945916cd2Sjpk call.callop = BCLEARVALID; 21045916cd2Sjpk clrvcall.clear = *clearance; 21145916cd2Sjpk 21245916cd2Sjpk if (__call_labeld(&callp, &bufsize, &datasize) != SUCCESS) { 21345916cd2Sjpk /* process error */ 21445916cd2Sjpk 21545916cd2Sjpk return (-1); 21645916cd2Sjpk } 21745916cd2Sjpk return (clrvret.valid); 21845916cd2Sjpk } 21945916cd2Sjpk #undef clrvcall 22045916cd2Sjpk #undef clrvret 22145916cd2Sjpk 22245916cd2Sjpk #define inforet callp->param.aret.rvals.info_ret 22345916cd2Sjpk /* 22445916cd2Sjpk * labelinfo - Get information about the label encodings file. 22545916cd2Sjpk * 22645916cd2Sjpk * Entry info = Address of label_info structure to update. 22745916cd2Sjpk * 22845916cd2Sjpk * Exit info = Updated. 22945916cd2Sjpk * 23045916cd2Sjpk * Returns -1, If unable to access label encodings file, or server failure. 23145916cd2Sjpk * 1, If successful. 23245916cd2Sjpk * 23345916cd2Sjpk * Calls __call_labeld(LABELINFO). 23445916cd2Sjpk */ 23545916cd2Sjpk 23645916cd2Sjpk int 23745916cd2Sjpk labelinfo(struct label_info *info) 23845916cd2Sjpk { 23945916cd2Sjpk labeld_data_t call; 24045916cd2Sjpk labeld_data_t *callp = &call; 24145916cd2Sjpk size_t bufsize = sizeof (labeld_data_t); 24245916cd2Sjpk size_t datasize = CALL_SIZE(info_call_t, 0); 24345916cd2Sjpk int rval; 24445916cd2Sjpk 24545916cd2Sjpk call.callop = LABELINFO; 24645916cd2Sjpk 24745916cd2Sjpk if ((rval = __call_labeld(&callp, &bufsize, &datasize)) != SUCCESS) { 24845916cd2Sjpk /* process error */ 24945916cd2Sjpk 25045916cd2Sjpk return (-1); 25145916cd2Sjpk } 25245916cd2Sjpk *info = inforet.info; 25345916cd2Sjpk return (rval); 25445916cd2Sjpk } 25545916cd2Sjpk #undef inforet 25645916cd2Sjpk 25745916cd2Sjpk #define lvret callp->param.aret.rvals.vers_ret 25845916cd2Sjpk /* 25945916cd2Sjpk * labelvers - Get version string of the label encodings file. 26045916cd2Sjpk * 26145916cd2Sjpk * Entry version = Address of string pointer to return. 26245916cd2Sjpk * len = Length of string if pre-allocated. 26345916cd2Sjpk * 26445916cd2Sjpk * Exit version = Updated. 26545916cd2Sjpk * 26645916cd2Sjpk * Returns -1, If unable to access label encodings file, or server failure. 26745916cd2Sjpk * 0, If unable to allocate version string, 26845916cd2Sjpk * or pre-allocated version string to short 26945916cd2Sjpk * (and **version = '\0'). 27045916cd2Sjpk * length (including null) of version string, If successful. 27145916cd2Sjpk * 27245916cd2Sjpk * Calls __call_labeld(LABELVERS) 27345916cd2Sjpk * malloc, strlen. 27445916cd2Sjpk */ 27545916cd2Sjpk 27645916cd2Sjpk ssize_t 27745916cd2Sjpk labelvers(char **version, size_t len) 27845916cd2Sjpk { 27945916cd2Sjpk labeld_data_t call; 28045916cd2Sjpk labeld_data_t *callp = &call; 28145916cd2Sjpk size_t bufsize = sizeof (labeld_data_t); 28245916cd2Sjpk size_t datasize = CALL_SIZE(vers_call_t, 0); 28345916cd2Sjpk size_t ver_len; 28445916cd2Sjpk 28545916cd2Sjpk call.callop = LABELVERS; 28645916cd2Sjpk 28745916cd2Sjpk if (__call_labeld(&callp, &bufsize, &datasize) != SUCCESS) { 28845916cd2Sjpk 28945916cd2Sjpk if (callp != &call) 29045916cd2Sjpk /* release return buffer */ 29145916cd2Sjpk (void) munmap((void *)callp, bufsize); 29245916cd2Sjpk return (-1); 29345916cd2Sjpk } 29445916cd2Sjpk 29545916cd2Sjpk /* unpack length */ 29645916cd2Sjpk 29745916cd2Sjpk ver_len = strlen(lvret.vers) + 1; 29845916cd2Sjpk if (*version == NULL) { 29945916cd2Sjpk if ((*version = malloc(ver_len)) == NULL) { 30045916cd2Sjpk if (callp != &call) 30145916cd2Sjpk /* release return buffer */ 30245916cd2Sjpk (void) munmap((void *)callp, bufsize); 30345916cd2Sjpk return (0); 30445916cd2Sjpk } 30545916cd2Sjpk } else if (ver_len > len) { 30645916cd2Sjpk **version = '\0'; 30745916cd2Sjpk if (callp != &call) 30845916cd2Sjpk /* release return buffer */ 30945916cd2Sjpk (void) munmap((void *)callp, bufsize); 31045916cd2Sjpk return (0); 31145916cd2Sjpk } 31245916cd2Sjpk (void) strcpy(*version, lvret.vers); 31345916cd2Sjpk 31445916cd2Sjpk if (callp != &call) 31545916cd2Sjpk /* release return buffer */ 31645916cd2Sjpk (void) munmap((void *)callp, bufsize); 31745916cd2Sjpk return (ver_len); 31845916cd2Sjpk } /* labelvers */ 31945916cd2Sjpk #undef lvret 32045916cd2Sjpk 32145916cd2Sjpk #define ccall callp->param.acall.cargs.color_arg 32245916cd2Sjpk #define cret callp->param.aret.rvals.color_ret 32345916cd2Sjpk /* 32445916cd2Sjpk * bltocolor - get ASCII color name of label. 32545916cd2Sjpk * 32645916cd2Sjpk * Entry label = Sensitivity Level of color to get. 32745916cd2Sjpk * size = Size of the color_name array. 32845916cd2Sjpk * color_name = Storage for ASCII color name string to be returned. 32945916cd2Sjpk * 33045916cd2Sjpk * Exit None. 33145916cd2Sjpk * 33245916cd2Sjpk * Returns NULL, If error (label encodings file not accessible, 33345916cd2Sjpk * invalid label, no color for this label). 33445916cd2Sjpk * Address of color_name parameter containing ASCII color name 33545916cd2Sjpk * defined for the label. 33645916cd2Sjpk * 33745916cd2Sjpk * Calls __call_labeld(BLTOCOLOR), strlen. 33845916cd2Sjpk */ 33945916cd2Sjpk 34045916cd2Sjpk char * 34145916cd2Sjpk bltocolor_r(const blevel_t *label, size_t size, char *color_name) 34245916cd2Sjpk { 34345916cd2Sjpk labeld_data_t call; 34445916cd2Sjpk labeld_data_t *callp = &call; 34545916cd2Sjpk size_t bufsize = sizeof (labeld_data_t); 34645916cd2Sjpk size_t datasize = CALL_SIZE(color_call_t, 0); 34745916cd2Sjpk char *colorp; 34845916cd2Sjpk 34945916cd2Sjpk call.callop = BLTOCOLOR; 35045916cd2Sjpk ccall.label = *label; 35145916cd2Sjpk 35245916cd2Sjpk if ((__call_labeld(&callp, &bufsize, &datasize) != SUCCESS) || 35345916cd2Sjpk (callp->reterr != 0) || 35445916cd2Sjpk (strlen(cret.color) >= size)) { 35545916cd2Sjpk 35645916cd2Sjpk if (callp != &call) 35745916cd2Sjpk /* release return buffer */ 35845916cd2Sjpk (void) munmap((void *)callp, bufsize); 35945916cd2Sjpk return (NULL); 36045916cd2Sjpk } 36145916cd2Sjpk 36245916cd2Sjpk colorp = strcpy(color_name, cret.color); 36345916cd2Sjpk 36445916cd2Sjpk if (callp != &call) 36545916cd2Sjpk /* release return buffer */ 36645916cd2Sjpk (void) munmap((void *)callp, bufsize); 36745916cd2Sjpk return (colorp); 36845916cd2Sjpk } /* bltocolor_r */ 36945916cd2Sjpk #undef ccall 37045916cd2Sjpk #undef cret 37145916cd2Sjpk 37245916cd2Sjpk /* 37345916cd2Sjpk * bltocolor - get ASCII color name of label. 37445916cd2Sjpk * 37545916cd2Sjpk * Entry label = Sensitivity Level of color to get. 37645916cd2Sjpk * 37745916cd2Sjpk * Exit None. 37845916cd2Sjpk * 37945916cd2Sjpk * Returns NULL, If error (label encodings file not accessible, 38045916cd2Sjpk * invalid label, no color for this label). 38145916cd2Sjpk * Address of statically allocated string containing ASCII 38245916cd2Sjpk * color name defined for the classification contained 38345916cd2Sjpk * in label. 38445916cd2Sjpk * 38545916cd2Sjpk * Uses color. 38645916cd2Sjpk * 38745916cd2Sjpk * Calls bltocolor_r. 38845916cd2Sjpk */ 38945916cd2Sjpk 39045916cd2Sjpk char * 39145916cd2Sjpk bltocolor(const blevel_t *label) 39245916cd2Sjpk { 39345916cd2Sjpk return (bltocolor_r(label, sizeof (color), color)); 39445916cd2Sjpk } /* bltocolor */ 39545916cd2Sjpk 39645916cd2Sjpk blevel_t * 39745916cd2Sjpk blabel_alloc(void) 39845916cd2Sjpk { 39945916cd2Sjpk return (m_label_alloc(MAC_LABEL)); 40045916cd2Sjpk } 40145916cd2Sjpk 40245916cd2Sjpk void 40345916cd2Sjpk blabel_free(blevel_t *label_p) 40445916cd2Sjpk { 40545916cd2Sjpk free(label_p); 40645916cd2Sjpk } 40745916cd2Sjpk 408*42096647STony Nguyen size32_t 40945916cd2Sjpk blabel_size(void) 41045916cd2Sjpk { 41145916cd2Sjpk return (sizeof (blevel_t)); 41245916cd2Sjpk } 41345916cd2Sjpk 41445916cd2Sjpk /* 41545916cd2Sjpk * getuserrange - get label range for user 41645916cd2Sjpk * 41745916cd2Sjpk * Entry username of user 41845916cd2Sjpk * 41945916cd2Sjpk * Exit None. 42045916cd2Sjpk * 42145916cd2Sjpk * Returns NULL, If memory allocation failure or userdefs failure. 42245916cd2Sjpk * otherwise returns the allocates m_range_t with the 42345916cd2Sjpk * user's min and max labels set. 42445916cd2Sjpk */ 42545916cd2Sjpk 42645916cd2Sjpk m_range_t * 42745916cd2Sjpk getuserrange(const char *username) 42845916cd2Sjpk { 42945916cd2Sjpk char *kv_str = NULL; 43045916cd2Sjpk userattr_t *userp = NULL; 43145916cd2Sjpk m_range_t *range; 432aa2e15f6Srica m_label_t *def_min, *def_clr; 43345916cd2Sjpk 43445916cd2Sjpk /* 43545916cd2Sjpk * Get some memory 43645916cd2Sjpk */ 43745916cd2Sjpk 43845916cd2Sjpk if ((range = malloc(sizeof (m_range_t))) == NULL) { 43945916cd2Sjpk return (NULL); 44045916cd2Sjpk } 44145916cd2Sjpk if ((range->lower_bound = m_label_alloc(MAC_LABEL)) == NULL) { 44245916cd2Sjpk free(range); 44345916cd2Sjpk return (NULL); 44445916cd2Sjpk } 445aa2e15f6Srica def_min = range->lower_bound; 44645916cd2Sjpk if ((range->upper_bound = m_label_alloc(USER_CLEAR)) == NULL) { 44745916cd2Sjpk m_label_free(range->lower_bound); 44845916cd2Sjpk free(range); 44945916cd2Sjpk return (NULL); 45045916cd2Sjpk } 451aa2e15f6Srica def_clr = range->upper_bound; 452aa2e15f6Srica 453aa2e15f6Srica /* If the user has an explicit min_label or clearance, use it. */ 454aa2e15f6Srica if ((userp = getusernam(username)) != NULL) { 455aa2e15f6Srica if ((kv_str = kva_match(userp->attr, USERATTR_MINLABEL)) 456aa2e15f6Srica != NULL) { 457aa2e15f6Srica (void) str_to_label(kv_str, &range->lower_bound, 458aa2e15f6Srica MAC_LABEL, L_NO_CORRECTION, NULL); 459aa2e15f6Srica def_min = NULL; /* don't get default later */ 460aa2e15f6Srica } 461aa2e15f6Srica if ((kv_str = kva_match(userp->attr, USERATTR_CLEARANCE)) 462aa2e15f6Srica != NULL) { 463aa2e15f6Srica (void) str_to_label(kv_str, &range->upper_bound, 464aa2e15f6Srica USER_CLEAR, L_NO_CORRECTION, NULL); 465aa2e15f6Srica def_clr = NULL; /* don't get default later */ 466aa2e15f6Srica } 467aa2e15f6Srica free_userattr(userp); 468aa2e15f6Srica } 469aa2e15f6Srica if (def_min || def_clr) { 470aa2e15f6Srica /* Need to use system default clearance and/or min_label */ 471aa2e15f6Srica if ((userdefs(def_min, def_clr)) == -1) { 47245916cd2Sjpk m_label_free(range->lower_bound); 47345916cd2Sjpk m_label_free(range->upper_bound); 47445916cd2Sjpk free(range); 47545916cd2Sjpk return (NULL); 47645916cd2Sjpk } 47745916cd2Sjpk } 478aa2e15f6Srica 47945916cd2Sjpk return (range); 48045916cd2Sjpk } 481