/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _KERNEL #include #include #include #include #include #else /* _KERNEL */ #include #include #endif /* _KERNEL */ #include static smb_sid_t *smb_sid_alloc(size_t); /* * smb_sid_isvalid * * Performs a minimal SID validation. */ boolean_t smb_sid_isvalid(smb_sid_t *sid) { if (sid == NULL) return (B_FALSE); return ((sid->sid_revision == NT_SID_REVISION) && (sid->sid_subauthcnt < NT_SID_SUBAUTH_MAX)); } /* * smb_sid_len * * Returns the number of bytes required to hold the sid. */ int smb_sid_len(smb_sid_t *sid) { if (sid == NULL) return (0); return (sizeof (smb_sid_t) - sizeof (uint32_t) + (sid->sid_subauthcnt * sizeof (uint32_t))); } /* * smb_sid_dup * * Make a duplicate of the specified sid. The memory for the new sid * should be freed by calling smb_sid_free(). * A pointer to the new sid is returned. */ smb_sid_t * smb_sid_dup(smb_sid_t *sid) { smb_sid_t *new_sid; int size; if (sid == NULL) return (NULL); size = smb_sid_len(sid); if ((new_sid = smb_sid_alloc(size)) == NULL) return (NULL); bcopy(sid, new_sid, size); return (new_sid); } /* * smb_sid_splice * * Make a full sid from a domain sid and a relative id (rid). * The memory for the result sid should be freed by calling * smb_sid_free(). A pointer to the new sid is returned. */ smb_sid_t * smb_sid_splice(smb_sid_t *domain_sid, uint32_t rid) { smb_sid_t *sid; int size; if (domain_sid == NULL) return (NULL); size = smb_sid_len(domain_sid); if ((sid = smb_sid_alloc(size + sizeof (rid))) == NULL) return (NULL); bcopy(domain_sid, sid, size); sid->sid_subauth[domain_sid->sid_subauthcnt] = rid; ++sid->sid_subauthcnt; return (sid); } /* * smb_sid_getrid * * Return the Relative Id (RID) of the specified SID. It is the * caller's responsibility to ensure that this is an appropriate SID. * All we do here is return the last sub-authority from the SID. */ int smb_sid_getrid(smb_sid_t *sid, uint32_t *rid) { if (!smb_sid_isvalid(sid) || (rid == NULL) || (sid->sid_subauthcnt == 0)) return (-1); *rid = sid->sid_subauth[sid->sid_subauthcnt - 1]; return (0); } /* * smb_sid_split * * Take a full sid and split it into a domain sid and a relative id (rid). * The domain SID is allocated and a pointer to it will be returned. The * RID value is passed back in 'rid' arg if it's not NULL. The allocated * memory for the domain SID must be freed by caller. */ smb_sid_t * smb_sid_split(smb_sid_t *sid, uint32_t *rid) { smb_sid_t *domsid; if (!smb_sid_isvalid(sid) || (sid->sid_subauthcnt == 0)) return (NULL); if ((domsid = smb_sid_dup(sid)) == NULL) return (NULL); --domsid->sid_subauthcnt; if (rid) *rid = domsid->sid_subauth[domsid->sid_subauthcnt]; return (domsid); } /* * smb_sid_splitstr * * Takes a full sid in string form and split it into a domain sid and a * relative id (rid). * * IMPORTANT: The original sid is modified in place. This function assumes * given SID is in valid string format. */ int smb_sid_splitstr(char *strsid, uint32_t *rid) { char *p; if ((p = strrchr(strsid, '-')) == NULL) return (-1); *p++ = '\0'; if (rid) { #ifdef _KERNEL unsigned long sua = 0; (void) ddi_strtoul(p, NULL, 10, &sua); *rid = (uint32_t)sua; #else *rid = strtoul(p, NULL, 10); #endif } return (0); } /* * smb_sid_cmp * * Compare two SIDs and return a boolean result. The checks are ordered * such that components that are more likely to differ are checked * first. For example, after checking that the SIDs contain the same * sid_subauthcnt, we check the sub-authorities in reverse order because * the RID is the most likely differentiator between two SIDs, i.e. * they are probably going to be in the same domain. */ boolean_t smb_sid_cmp(smb_sid_t *sid1, smb_sid_t *sid2) { int i; if (sid1 == NULL || sid2 == NULL) return (B_FALSE); if (sid1->sid_subauthcnt != sid2->sid_subauthcnt || sid1->sid_revision != sid2->sid_revision) return (B_FALSE); for (i = sid1->sid_subauthcnt - 1; i >= 0; --i) if (sid1->sid_subauth[i] != sid2->sid_subauth[i]) return (B_FALSE); if (bcmp(&sid1->sid_authority, &sid2->sid_authority, NT_SID_AUTH_MAX)) return (B_FALSE); return (B_TRUE); } /* * smb_sid_indomain * * Check if given SID is in given domain. */ boolean_t smb_sid_indomain(smb_sid_t *domain_sid, smb_sid_t *sid) { int i; if (sid == NULL || domain_sid == NULL) return (B_FALSE); if (domain_sid->sid_revision != sid->sid_revision || sid->sid_subauthcnt < domain_sid->sid_subauthcnt) return (B_FALSE); for (i = domain_sid->sid_subauthcnt - 1; i >= 0; --i) if (domain_sid->sid_subauth[i] != sid->sid_subauth[i]) return (B_FALSE); if (bcmp(&domain_sid->sid_authority, &sid->sid_authority, NT_SID_AUTH_MAX)) return (B_FALSE); return (B_TRUE); } #ifndef _KERNEL /* * smb_sid_islocal * * Check a SID to see if it belongs to the local domain. */ boolean_t smb_sid_islocal(smb_sid_t *sid) { smb_domain_t di; boolean_t islocal = B_FALSE; if (smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di)) islocal = smb_sid_indomain(di.di_binsid, sid); return (islocal); } #endif /* _KERNEL */ /* * smb_sid_tostr * * Fill in the passed buffer with the string form of the given * binary sid. */ void smb_sid_tostr(const smb_sid_t *sid, char *strsid) { char *p = strsid; int i; if (sid == NULL || strsid == NULL) return; (void) sprintf(p, "S-%d-", sid->sid_revision); while (*p) p++; for (i = 0; i < NT_SID_AUTH_MAX; ++i) { if (sid->sid_authority[i] != 0 || i == NT_SID_AUTH_MAX - 1) { (void) sprintf(p, "%d", sid->sid_authority[i]); while (*p) p++; } } for (i = 0; i < sid->sid_subauthcnt && i < NT_SID_SUBAUTH_MAX; ++i) { (void) sprintf(p, "-%u", sid->sid_subauth[i]); while (*p) p++; } } /* * smb_sid_fromstr * * Converts a SID in string form to a SID structure. There are lots of * simplifying assumptions in here. The memory for the SID is allocated * as if it was the largest possible SID; the caller is responsible for * freeing the memory when it is no longer required. We assume that the * string starts with "S-1-" and that the authority is held in the last * byte, which should be okay for most situations. It also assumes the * sub-authorities are in decimal format. * * On success, a pointer to a SID is returned. Otherwise a null pointer * is returned. */ #ifdef _KERNEL smb_sid_t * smb_sid_fromstr(const char *sidstr) { smb_sid_t *sid; smb_sid_t *retsid; const char *p; int size; uint8_t i; unsigned long sua; if (sidstr == NULL) return (NULL); if (strncmp(sidstr, "S-1-", 4) != 0) return (NULL); size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t)); sid = kmem_zalloc(size, KM_SLEEP); sid->sid_revision = NT_SID_REVISION; sua = 0; (void) ddi_strtoul(&sidstr[4], 0, 10, &sua); sid->sid_authority[5] = (uint8_t)sua; for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) { while (*p && *p == '-') ++p; if (*p < '0' || *p > '9') { kmem_free(sid, size); return (NULL); } sua = 0; (void) ddi_strtoul(p, 0, 10, &sua); sid->sid_subauth[i] = (uint32_t)sua; while (*p && *p != '-') ++p; } sid->sid_subauthcnt = i; retsid = smb_sid_dup(sid); kmem_free(sid, size); return (retsid); } #else /* _KERNEL */ smb_sid_t * smb_sid_fromstr(const char *sidstr) { smb_sid_t *sid; const char *p; int size; uint8_t i; if (sidstr == NULL) return (NULL); if (strncmp(sidstr, "S-1-", 4) != 0) return (NULL); size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t)); if ((sid = malloc(size)) == NULL) return (NULL); bzero(sid, size); sid->sid_revision = NT_SID_REVISION; sid->sid_authority[5] = atoi(&sidstr[4]); for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) { while (*p && *p == '-') ++p; if (*p < '0' || *p > '9') { free(sid); return (NULL); } sid->sid_subauth[i] = strtoul(p, NULL, 10); while (*p && *p != '-') ++p; } sid->sid_subauthcnt = i; return (sid); } #endif /* _KERNEL */ /* * smb_sid_type2str * * Returns the text name for a SID_NAME_USE value. The SID_NAME_USE * provides the context for a SID, i.e. the type of resource to which * it refers. */ char * smb_sid_type2str(uint16_t snu_id) { static char *snu_name[] = { "SidTypeSidPrefix", "SidTypeUser", "SidTypeGroup", "SidTypeDomain", "SidTypeAlias", "SidTypeWellKnownGroup", "SidTypeDeletedAccount", "SidTypeInvalid", "SidTypeUnknown", "SidTypeComputer", "SidTypeLabel" }; if (snu_id < ((sizeof (snu_name)/sizeof (snu_name[0])))) return (snu_name[snu_id]); return (snu_name[SidTypeUnknown]); } static smb_sid_t * smb_sid_alloc(size_t size) { smb_sid_t *sid; #ifdef _KERNEL sid = kmem_alloc(size, KM_SLEEP); #else sid = malloc(size); #endif return (sid); } void smb_sid_free(smb_sid_t *sid) { #ifdef _KERNEL if (sid == NULL) return; kmem_free(sid, smb_sid_len(sid)); #else free(sid); #endif } #ifndef _KERNEL void smb_ids_free(smb_ids_t *ids) { smb_id_t *id; int i; if ((ids != NULL) && (ids->i_ids != NULL)) { id = ids->i_ids; for (i = 0; i < ids->i_cnt; i++, id++) smb_sid_free(id->i_sid); free(ids->i_ids); } } #endif