1da6c28aaSamw /* 2da6c28aaSamw * CDDL HEADER START 3da6c28aaSamw * 4da6c28aaSamw * The contents of this file are subject to the terms of the 5da6c28aaSamw * Common Development and Distribution License (the "License"). 6da6c28aaSamw * You may not use this file except in compliance with the License. 7da6c28aaSamw * 8da6c28aaSamw * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9da6c28aaSamw * or http://www.opensolaris.org/os/licensing. 10da6c28aaSamw * See the License for the specific language governing permissions 11da6c28aaSamw * and limitations under the License. 12da6c28aaSamw * 13da6c28aaSamw * When distributing Covered Code, include this CDDL HEADER in each 14da6c28aaSamw * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15da6c28aaSamw * If applicable, add the following below this CDDL HEADER, with the 16da6c28aaSamw * fields enclosed by brackets "[]" replaced with your own identifying 17da6c28aaSamw * information: Portions Copyright [yyyy] [name of copyright owner] 18da6c28aaSamw * 19da6c28aaSamw * CDDL HEADER END 20da6c28aaSamw */ 21da6c28aaSamw /* 22*7f667e74Sjose borrego * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23da6c28aaSamw * Use is subject to license terms. 24da6c28aaSamw */ 25da6c28aaSamw 26da6c28aaSamw #ifndef _KERNEL 27da6c28aaSamw #include <stdio.h> 28da6c28aaSamw #include <strings.h> 29da6c28aaSamw #include <stdlib.h> 30da6c28aaSamw #include <syslog.h> 31da6c28aaSamw #include <smbsrv/libsmb.h> 32da6c28aaSamw #else /* _KERNEL */ 33da6c28aaSamw #include <sys/types.h> 34da6c28aaSamw #include <sys/sunddi.h> 35da6c28aaSamw #endif /* _KERNEL */ 36da6c28aaSamw 376537f381Sas200622 #include <smbsrv/smb_sid.h> 38da6c28aaSamw 396537f381Sas200622 static smb_sid_t *smb_sid_alloc(size_t); 40da6c28aaSamw 41da6c28aaSamw /* 426537f381Sas200622 * smb_sid_isvalid 43da6c28aaSamw * 446537f381Sas200622 * Performs a minimal SID validation. 45da6c28aaSamw */ 466537f381Sas200622 boolean_t 476537f381Sas200622 smb_sid_isvalid(smb_sid_t *sid) 48da6c28aaSamw { 496537f381Sas200622 if (sid == NULL) 506537f381Sas200622 return (B_FALSE); 51da6c28aaSamw 526537f381Sas200622 return ((sid->sid_revision == NT_SID_REVISION) && 536537f381Sas200622 (sid->sid_subauthcnt < NT_SID_SUBAUTH_MAX)); 54da6c28aaSamw } 55da6c28aaSamw 56da6c28aaSamw /* 576537f381Sas200622 * smb_sid_len 58da6c28aaSamw * 59da6c28aaSamw * Returns the number of bytes required to hold the sid. 60da6c28aaSamw */ 61da6c28aaSamw int 626537f381Sas200622 smb_sid_len(smb_sid_t *sid) 63da6c28aaSamw { 646537f381Sas200622 if (sid == NULL) 65da6c28aaSamw return (0); 66da6c28aaSamw 676537f381Sas200622 return (sizeof (smb_sid_t) - sizeof (uint32_t) 686537f381Sas200622 + (sid->sid_subauthcnt * sizeof (uint32_t))); 69da6c28aaSamw } 70da6c28aaSamw 71da6c28aaSamw /* 726537f381Sas200622 * smb_sid_dup 73da6c28aaSamw * 746537f381Sas200622 * Make a duplicate of the specified sid. The memory for the new sid 756537f381Sas200622 * should be freed by calling smb_sid_free(). 766537f381Sas200622 * A pointer to the new sid is returned. 77da6c28aaSamw */ 786537f381Sas200622 smb_sid_t * 796537f381Sas200622 smb_sid_dup(smb_sid_t *sid) 80da6c28aaSamw { 816537f381Sas200622 smb_sid_t *new_sid; 82da6c28aaSamw int size; 83da6c28aaSamw 846537f381Sas200622 if (sid == NULL) 856537f381Sas200622 return (NULL); 86da6c28aaSamw 876537f381Sas200622 size = smb_sid_len(sid); 886537f381Sas200622 if ((new_sid = smb_sid_alloc(size)) == NULL) 896537f381Sas200622 return (NULL); 90da6c28aaSamw 916537f381Sas200622 bcopy(sid, new_sid, size); 92da6c28aaSamw return (new_sid); 93da6c28aaSamw } 94da6c28aaSamw 95da6c28aaSamw 96da6c28aaSamw /* 976537f381Sas200622 * smb_sid_splice 98da6c28aaSamw * 996537f381Sas200622 * Make a full sid from a domain sid and a relative id (rid). 1006537f381Sas200622 * The memory for the result sid should be freed by calling 1016537f381Sas200622 * smb_sid_free(). A pointer to the new sid is returned. 102da6c28aaSamw */ 1036537f381Sas200622 smb_sid_t * 1046537f381Sas200622 smb_sid_splice(smb_sid_t *domain_sid, uint32_t rid) 105da6c28aaSamw { 1066537f381Sas200622 smb_sid_t *sid; 107da6c28aaSamw int size; 108da6c28aaSamw 1096537f381Sas200622 if (domain_sid == NULL) 1106537f381Sas200622 return (NULL); 111da6c28aaSamw 1126537f381Sas200622 size = smb_sid_len(domain_sid); 1136537f381Sas200622 if ((sid = smb_sid_alloc(size + sizeof (rid))) == NULL) 1146537f381Sas200622 return (NULL); 115da6c28aaSamw 1166537f381Sas200622 bcopy(domain_sid, sid, size); 117da6c28aaSamw 1186537f381Sas200622 sid->sid_subauth[domain_sid->sid_subauthcnt] = rid; 1196537f381Sas200622 ++sid->sid_subauthcnt; 120da6c28aaSamw 121da6c28aaSamw return (sid); 122da6c28aaSamw } 123da6c28aaSamw 124da6c28aaSamw /* 1256537f381Sas200622 * smb_sid_getrid 126da6c28aaSamw * 1276537f381Sas200622 * Return the Relative Id (RID) of the specified SID. It is the 128da6c28aaSamw * caller's responsibility to ensure that this is an appropriate SID. 129da6c28aaSamw * All we do here is return the last sub-authority from the SID. 130da6c28aaSamw */ 131da6c28aaSamw int 1326537f381Sas200622 smb_sid_getrid(smb_sid_t *sid, uint32_t *rid) 133da6c28aaSamw { 1346537f381Sas200622 if (!smb_sid_isvalid(sid) || (rid == NULL) || 1356537f381Sas200622 (sid->sid_subauthcnt == 0)) 136da6c28aaSamw return (-1); 137da6c28aaSamw 1386537f381Sas200622 *rid = sid->sid_subauth[sid->sid_subauthcnt - 1]; 139da6c28aaSamw return (0); 140da6c28aaSamw } 141da6c28aaSamw 142da6c28aaSamw /* 1436537f381Sas200622 * smb_sid_split 144da6c28aaSamw * 1456537f381Sas200622 * Take a full sid and split it into a domain sid and a relative id (rid). 146*7f667e74Sjose borrego * The domain SID is allocated and a pointer to it will be returned. The 147*7f667e74Sjose borrego * RID value is passed back in 'rid' arg if it's not NULL. The allocated 148*7f667e74Sjose borrego * memory for the domain SID must be freed by caller. 149da6c28aaSamw */ 150*7f667e74Sjose borrego smb_sid_t * 1516537f381Sas200622 smb_sid_split(smb_sid_t *sid, uint32_t *rid) 152da6c28aaSamw { 153*7f667e74Sjose borrego smb_sid_t *domsid; 154da6c28aaSamw 155*7f667e74Sjose borrego if (!smb_sid_isvalid(sid) || (sid->sid_subauthcnt == 0)) 156*7f667e74Sjose borrego return (NULL); 157*7f667e74Sjose borrego 158*7f667e74Sjose borrego if ((domsid = smb_sid_dup(sid)) == NULL) 159*7f667e74Sjose borrego return (NULL); 160*7f667e74Sjose borrego 161*7f667e74Sjose borrego --domsid->sid_subauthcnt; 162da6c28aaSamw if (rid) 163*7f667e74Sjose borrego *rid = domsid->sid_subauth[domsid->sid_subauthcnt]; 164*7f667e74Sjose borrego 165*7f667e74Sjose borrego return (domsid); 166da6c28aaSamw } 167da6c28aaSamw 168da6c28aaSamw /* 1696537f381Sas200622 * smb_sid_splitstr 170da6c28aaSamw * 1716537f381Sas200622 * Takes a full sid in string form and split it into a domain sid and a 1726537f381Sas200622 * relative id (rid). 1736537f381Sas200622 * 1746537f381Sas200622 * IMPORTANT: The original sid is modified in place. This function assumes 1756537f381Sas200622 * given SID is in valid string format. 176da6c28aaSamw */ 1776537f381Sas200622 int 1786537f381Sas200622 smb_sid_splitstr(char *strsid, uint32_t *rid) 179da6c28aaSamw { 1806537f381Sas200622 char *p; 181da6c28aaSamw 1826537f381Sas200622 if ((p = strrchr(strsid, '-')) == NULL) 1836537f381Sas200622 return (-1); 184da6c28aaSamw 1856537f381Sas200622 *p++ = '\0'; 1866537f381Sas200622 if (rid) { 1876537f381Sas200622 #ifdef _KERNEL 1886537f381Sas200622 unsigned long sua = 0; 1896537f381Sas200622 (void) ddi_strtoul(p, NULL, 10, &sua); 1906537f381Sas200622 *rid = (uint32_t)sua; 1916537f381Sas200622 #else 1926537f381Sas200622 *rid = strtoul(p, NULL, 10); 1936537f381Sas200622 #endif 1946537f381Sas200622 } 1956537f381Sas200622 196da6c28aaSamw return (0); 197da6c28aaSamw } 198da6c28aaSamw 199da6c28aaSamw /* 2006537f381Sas200622 * smb_sid_cmp 201da6c28aaSamw * 202da6c28aaSamw * Compare two SIDs and return a boolean result. The checks are ordered 203da6c28aaSamw * such that components that are more likely to differ are checked 204da6c28aaSamw * first. For example, after checking that the SIDs contain the same 2056537f381Sas200622 * sid_subauthcnt, we check the sub-authorities in reverse order because 206da6c28aaSamw * the RID is the most likely differentiator between two SIDs, i.e. 207da6c28aaSamw * they are probably going to be in the same domain. 208da6c28aaSamw */ 2096537f381Sas200622 boolean_t 2106537f381Sas200622 smb_sid_cmp(smb_sid_t *sid1, smb_sid_t *sid2) 211da6c28aaSamw { 212da6c28aaSamw int i; 213da6c28aaSamw 2146537f381Sas200622 if (sid1 == NULL || sid2 == NULL) 2156537f381Sas200622 return (B_FALSE); 216da6c28aaSamw 2176537f381Sas200622 if (sid1->sid_subauthcnt != sid2->sid_subauthcnt || 2186537f381Sas200622 sid1->sid_revision != sid2->sid_revision) 2196537f381Sas200622 return (B_FALSE); 220da6c28aaSamw 2216537f381Sas200622 for (i = sid1->sid_subauthcnt - 1; i >= 0; --i) 2226537f381Sas200622 if (sid1->sid_subauth[i] != sid2->sid_subauth[i]) 2236537f381Sas200622 return (B_FALSE); 224da6c28aaSamw 2256537f381Sas200622 if (bcmp(&sid1->sid_authority, &sid2->sid_authority, NT_SID_AUTH_MAX)) 2266537f381Sas200622 return (B_FALSE); 227da6c28aaSamw 2286537f381Sas200622 return (B_TRUE); 229da6c28aaSamw } 230da6c28aaSamw 231da6c28aaSamw /* 2326537f381Sas200622 * smb_sid_indomain 233da6c28aaSamw * 234da6c28aaSamw * Check if given SID is in given domain. 235da6c28aaSamw */ 2366537f381Sas200622 boolean_t 2376537f381Sas200622 smb_sid_indomain(smb_sid_t *domain_sid, smb_sid_t *sid) 238da6c28aaSamw { 239da6c28aaSamw int i; 240da6c28aaSamw 2416537f381Sas200622 if (sid == NULL || domain_sid == NULL) 2426537f381Sas200622 return (B_FALSE); 243da6c28aaSamw 2446537f381Sas200622 if (domain_sid->sid_revision != sid->sid_revision || 2456537f381Sas200622 sid->sid_subauthcnt < domain_sid->sid_subauthcnt) 2466537f381Sas200622 return (B_FALSE); 247da6c28aaSamw 2486537f381Sas200622 for (i = domain_sid->sid_subauthcnt - 1; i >= 0; --i) 2496537f381Sas200622 if (domain_sid->sid_subauth[i] != sid->sid_subauth[i]) 2506537f381Sas200622 return (B_FALSE); 251da6c28aaSamw 2526537f381Sas200622 if (bcmp(&domain_sid->sid_authority, &sid->sid_authority, 2536537f381Sas200622 NT_SID_AUTH_MAX)) 2546537f381Sas200622 return (B_FALSE); 255da6c28aaSamw 2566537f381Sas200622 return (B_TRUE); 257da6c28aaSamw } 258da6c28aaSamw 259da6c28aaSamw #ifndef _KERNEL 260da6c28aaSamw /* 2616537f381Sas200622 * smb_sid_islocal 262da6c28aaSamw * 2636537f381Sas200622 * Check a SID to see if it belongs to the local domain. 264da6c28aaSamw */ 2656537f381Sas200622 boolean_t 2666537f381Sas200622 smb_sid_islocal(smb_sid_t *sid) 267da6c28aaSamw { 2686537f381Sas200622 return (smb_sid_indomain(nt_domain_local_sid(), sid)); 269da6c28aaSamw } 270da6c28aaSamw #endif /* _KERNEL */ 271da6c28aaSamw 272da6c28aaSamw /* 2736537f381Sas200622 * smb_sid_tostr 274da6c28aaSamw * 2756537f381Sas200622 * Fill in the passed buffer with the string form of the given 2766537f381Sas200622 * binary sid. 277da6c28aaSamw */ 278da6c28aaSamw void 2796537f381Sas200622 smb_sid_tostr(smb_sid_t *sid, char *strsid) 280da6c28aaSamw { 2816537f381Sas200622 char *p = strsid; 2826537f381Sas200622 int i; 283da6c28aaSamw 2846537f381Sas200622 if (sid == NULL || strsid == NULL) 285da6c28aaSamw return; 286da6c28aaSamw 2876537f381Sas200622 (void) sprintf(p, "S-%d-", sid->sid_revision); 288da6c28aaSamw while (*p) 2896537f381Sas200622 p++; 290da6c28aaSamw 291da6c28aaSamw for (i = 0; i < NT_SID_AUTH_MAX; ++i) { 2926537f381Sas200622 if (sid->sid_authority[i] != 0 || i == NT_SID_AUTH_MAX - 1) { 2936537f381Sas200622 (void) sprintf(p, "%d", sid->sid_authority[i]); 294da6c28aaSamw while (*p) 2956537f381Sas200622 p++; 296da6c28aaSamw } 297da6c28aaSamw } 298da6c28aaSamw 2996537f381Sas200622 for (i = 0; i < sid->sid_subauthcnt && i < NT_SID_SUBAUTH_MAX; ++i) { 3006537f381Sas200622 (void) sprintf(p, "-%u", sid->sid_subauth[i]); 301da6c28aaSamw while (*p) 3026537f381Sas200622 p++; 303da6c28aaSamw } 304da6c28aaSamw } 305da6c28aaSamw 306da6c28aaSamw /* 3076537f381Sas200622 * smb_sid_fromstr 308da6c28aaSamw * 309da6c28aaSamw * Converts a SID in string form to a SID structure. There are lots of 310da6c28aaSamw * simplifying assumptions in here. The memory for the SID is allocated 311da6c28aaSamw * as if it was the largest possible SID; the caller is responsible for 312da6c28aaSamw * freeing the memory when it is no longer required. We assume that the 313da6c28aaSamw * string starts with "S-1-" and that the authority is held in the last 314da6c28aaSamw * byte, which should be okay for most situations. It also assumes the 315da6c28aaSamw * sub-authorities are in decimal format. 316da6c28aaSamw * 317da6c28aaSamw * On success, a pointer to a SID is returned. Otherwise a null pointer 318da6c28aaSamw * is returned. 319da6c28aaSamw */ 3206537f381Sas200622 #ifdef _KERNEL 3216537f381Sas200622 smb_sid_t * 3226537f381Sas200622 smb_sid_fromstr(char *sidstr) 323da6c28aaSamw { 3246537f381Sas200622 smb_sid_t *sid; 3256537f381Sas200622 smb_sid_t *retsid; 326da6c28aaSamw char *p; 327da6c28aaSamw int size; 3286537f381Sas200622 uint8_t i; 3296537f381Sas200622 unsigned long sua; 330da6c28aaSamw 3316537f381Sas200622 if (sidstr == NULL) 3326537f381Sas200622 return (NULL); 333da6c28aaSamw 3346537f381Sas200622 if (strncmp(sidstr, "S-1-", 4) != 0) 3356537f381Sas200622 return (NULL); 336da6c28aaSamw 3376537f381Sas200622 size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t)); 3386537f381Sas200622 sid = kmem_zalloc(size, KM_SLEEP); 339da6c28aaSamw 3406537f381Sas200622 sid->sid_revision = NT_SID_REVISION; 341da6c28aaSamw sua = 0; 3426537f381Sas200622 (void) ddi_strtoul(&sidstr[4], 0, 10, &sua); 3436537f381Sas200622 sid->sid_authority[5] = (uint8_t)sua; 344da6c28aaSamw 345da6c28aaSamw for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) { 346da6c28aaSamw while (*p && *p == '-') 347da6c28aaSamw ++p; 348da6c28aaSamw 349da6c28aaSamw if (*p < '0' || *p > '9') { 3506537f381Sas200622 kmem_free(sid, size); 3516537f381Sas200622 return (NULL); 352da6c28aaSamw } 353da6c28aaSamw 354da6c28aaSamw sua = 0; 3556537f381Sas200622 (void) ddi_strtoul(p, 0, 10, &sua); 3566537f381Sas200622 sid->sid_subauth[i] = (uint32_t)sua; 357da6c28aaSamw 358da6c28aaSamw while (*p && *p != '-') 359da6c28aaSamw ++p; 360da6c28aaSamw } 361da6c28aaSamw 3626537f381Sas200622 sid->sid_subauthcnt = i; 3636537f381Sas200622 retsid = smb_sid_dup(sid); 3646537f381Sas200622 kmem_free(sid, size); 3656537f381Sas200622 3666537f381Sas200622 return (retsid); 3676537f381Sas200622 } 3686537f381Sas200622 #else /* _KERNEL */ 3696537f381Sas200622 smb_sid_t * 3706537f381Sas200622 smb_sid_fromstr(char *sidstr) 3716537f381Sas200622 { 3726537f381Sas200622 smb_sid_t *sid; 3736537f381Sas200622 char *p; 3746537f381Sas200622 int size; 3756537f381Sas200622 uint8_t i; 3766537f381Sas200622 3776537f381Sas200622 if (sidstr == NULL) 3786537f381Sas200622 return (NULL); 3796537f381Sas200622 3806537f381Sas200622 if (strncmp(sidstr, "S-1-", 4) != 0) 3816537f381Sas200622 return (NULL); 3826537f381Sas200622 3836537f381Sas200622 size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t)); 3846537f381Sas200622 3856537f381Sas200622 if ((sid = malloc(size)) == NULL) 3866537f381Sas200622 return (NULL); 3876537f381Sas200622 3886537f381Sas200622 bzero(sid, size); 3896537f381Sas200622 sid->sid_revision = NT_SID_REVISION; 3906537f381Sas200622 sid->sid_authority[5] = atoi(&sidstr[4]); 3916537f381Sas200622 3926537f381Sas200622 for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) { 3936537f381Sas200622 while (*p && *p == '-') 3946537f381Sas200622 ++p; 3956537f381Sas200622 3966537f381Sas200622 if (*p < '0' || *p > '9') { 3976537f381Sas200622 free(sid); 3986537f381Sas200622 return (NULL); 399da6c28aaSamw } 400da6c28aaSamw 4016537f381Sas200622 sid->sid_subauth[i] = strtoul(p, NULL, 10); 4026537f381Sas200622 4036537f381Sas200622 while (*p && *p != '-') 4046537f381Sas200622 ++p; 4056537f381Sas200622 } 4066537f381Sas200622 4076537f381Sas200622 sid->sid_subauthcnt = i; 4086537f381Sas200622 return (sid); 4096537f381Sas200622 } 4106537f381Sas200622 #endif /* _KERNEL */ 411da6c28aaSamw 412da6c28aaSamw /* 4136537f381Sas200622 * smb_sid_type2str 414da6c28aaSamw * 415da6c28aaSamw * Returns the text name for a SID_NAME_USE value. The SID_NAME_USE 416da6c28aaSamw * provides the context for a SID, i.e. the type of resource to which 417da6c28aaSamw * it refers. 418da6c28aaSamw */ 419da6c28aaSamw char * 4206537f381Sas200622 smb_sid_type2str(uint16_t snu_id) 421da6c28aaSamw { 422da6c28aaSamw static char *snu_name[] = { 423da6c28aaSamw "SidTypeSidPrefix", 424da6c28aaSamw "SidTypeUser", 425da6c28aaSamw "SidTypeGroup", 426da6c28aaSamw "SidTypeDomain", 427da6c28aaSamw "SidTypeAlias", 428da6c28aaSamw "SidTypeWellKnownGroup", 429da6c28aaSamw "SidTypeDeletedAccount", 430da6c28aaSamw "SidTypeInvalid", 431da6c28aaSamw "SidTypeUnknown" 432da6c28aaSamw }; 433da6c28aaSamw 434da6c28aaSamw if (snu_id < ((sizeof (snu_name)/sizeof (snu_name[0])))) 435da6c28aaSamw return (snu_name[snu_id]); 4366537f381Sas200622 437da6c28aaSamw return (snu_name[SidTypeUnknown]); 438da6c28aaSamw } 4396537f381Sas200622 4406537f381Sas200622 static smb_sid_t * 4416537f381Sas200622 smb_sid_alloc(size_t size) 4426537f381Sas200622 { 4436537f381Sas200622 smb_sid_t *sid; 4446537f381Sas200622 #ifdef _KERNEL 4456537f381Sas200622 sid = kmem_alloc(size, KM_SLEEP); 4466537f381Sas200622 #else 4476537f381Sas200622 sid = malloc(size); 4486537f381Sas200622 #endif 4496537f381Sas200622 return (sid); 450da6c28aaSamw } 451da6c28aaSamw 4526537f381Sas200622 void 4536537f381Sas200622 smb_sid_free(smb_sid_t *sid) 454da6c28aaSamw { 4556537f381Sas200622 #ifdef _KERNEL 4566537f381Sas200622 if (sid == NULL) 4576537f381Sas200622 return; 458da6c28aaSamw 4596537f381Sas200622 kmem_free(sid, smb_sid_len(sid)); 4606537f381Sas200622 #else 4616537f381Sas200622 free(sid); 4626537f381Sas200622 #endif 463da6c28aaSamw } 464*7f667e74Sjose borrego 465*7f667e74Sjose borrego #ifndef _KERNEL 466*7f667e74Sjose borrego void 467*7f667e74Sjose borrego smb_ids_free(smb_ids_t *ids) 468*7f667e74Sjose borrego { 469*7f667e74Sjose borrego smb_id_t *id; 470*7f667e74Sjose borrego int i; 471*7f667e74Sjose borrego 472*7f667e74Sjose borrego if ((ids != NULL) && (ids->i_ids != NULL)) { 473*7f667e74Sjose borrego id = ids->i_ids; 474*7f667e74Sjose borrego for (i = 0; i < ids->i_cnt; i++, id++) 475*7f667e74Sjose borrego smb_sid_free(id->i_sid); 476*7f667e74Sjose borrego 477*7f667e74Sjose borrego free(ids->i_ids); 478*7f667e74Sjose borrego } 479*7f667e74Sjose borrego } 480*7f667e74Sjose borrego #endif 481