/* * 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. * Copyright 2022 Tintri by DDN, Inc. All rights reserved. */ #if !defined(_KERNEL) && !defined(_FAKE_KERNEL) #include #include #include #include #else /* !_KERNEL && !_FAKE_KERNEL */ #include /* Needed for _FAKE_KERNEL */ #include #include #include #endif /* !_KERNEL && !_FAKE_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; int size; if (!smb_sid_isvalid(sid) || (sid->sid_subauthcnt == 0)) return (NULL); /* * We will reduce sid_subauthcnt by one, because * the domain SID does not include the RID. */ size = smb_sid_len(sid) - sizeof (uint32_t); if ((domsid = smb_sid_alloc(size)) == NULL) return (NULL); bcopy(sid, domsid, size); domsid->sid_subauthcnt = sid->sid_subauthcnt - 1; if (rid) *rid = sid->sid_subauth[sid->sid_subauthcnt - 1]; 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) { #if defined(_KERNEL) || defined(_FAKE_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); } /* * 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. */ #if defined(_KERNEL) || defined(_FAKE_KERNEL) smb_sid_t * smb_sid_fromstr(const char *sidstr) { smb_sid_t *sid; smb_sid_t *retsid = NULL; 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); sua = 0; (void) ddi_strtoul(&sidstr[4], (char **)&p, 10, &sua); /* * If ddi_strtoul() did the right thing, *p will point at the first '-' * after the identifier authority. * The IdentifierAuthority can be up to 2^48, but all known ones * currently fit into a uint8_t. * TODO: support IdentifierAuthorities > 255 (those over UINT32_MAX are * hex-formatted). */ if (sua > UINT8_MAX || (*p != '-' && *p != '\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; sid->sid_authority[5] = (uint8_t)sua; for (i = 0; i < NT_SID_SUBAUTH_MAX && *p; ++i) { while (*p == '-') ++p; if (*p < '0' || *p > '9') goto out; sua = 0; (void) ddi_strtoul(p, (char **)&p, 10, &sua); if (sua > UINT32_MAX) goto out; sid->sid_subauth[i] = (uint32_t)sua; if (*p != '\0' && *p != '-') goto out; } sid->sid_subauthcnt = i; retsid = smb_sid_dup(sid); out: 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; unsigned long sua; if (sidstr == NULL) return (NULL); if (strncmp(sidstr, "S-1-", 4) != 0) return (NULL); sua = strtoul(&sidstr[4], (char **)&p, 10); /* * If strtoul() did the right thing, *p will point at the first '-' * after the identifier authority. * The IdentifierAuthority can be up to 2^48, but all known ones * currently fit into a uint8_t. * TODO: support IdentifierAuthorities > 255 (those over UINT32_MAX are * hex-formatted). */ if (sua > UINT8_MAX || (*p != '-' && *p != '\0')) return (NULL); size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t)); if ((sid = calloc(size, 1)) == NULL) return (NULL); sid->sid_revision = NT_SID_REVISION; sid->sid_authority[5] = (uint8_t)sua; for (i = 0; i < NT_SID_SUBAUTH_MAX && *p; ++i) { while (*p == '-') ++p; if (*p < '0' || *p > '9') { free(sid); return (NULL); } sid->sid_subauth[i] = strtoul(p, (char **)&p, 10); if (*p != '\0' && *p != '-') { free(sid); return (NULL); } } 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; #if defined(_KERNEL) || defined(_FAKE_KERNEL) sid = kmem_alloc(size, KM_SLEEP); #else sid = malloc(size); #endif return (sid); } void smb_sid_free(smb_sid_t *sid) { #if defined(_KERNEL) || defined(_FAKE_KERNEL) if (sid == NULL) return; kmem_free(sid, smb_sid_len(sid)); #else free(sid); #endif }