xref: /onnv-gate/usr/src/common/smbsrv/smb_sid.c (revision 12508:edb7861a1533)
15331Samw /*
25331Samw  * CDDL HEADER START
35331Samw  *
45331Samw  * The contents of this file are subject to the terms of the
55331Samw  * Common Development and Distribution License (the "License").
65331Samw  * You may not use this file except in compliance with the License.
75331Samw  *
85331Samw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95331Samw  * or http://www.opensolaris.org/os/licensing.
105331Samw  * See the License for the specific language governing permissions
115331Samw  * and limitations under the License.
125331Samw  *
135331Samw  * When distributing Covered Code, include this CDDL HEADER in each
145331Samw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155331Samw  * If applicable, add the following below this CDDL HEADER, with the
165331Samw  * fields enclosed by brackets "[]" replaced with your own identifying
175331Samw  * information: Portions Copyright [yyyy] [name of copyright owner]
185331Samw  *
195331Samw  * CDDL HEADER END
205331Samw  */
215331Samw /*
22*12508Samw@Sun.COM  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
235331Samw  */
245331Samw 
255331Samw #ifndef _KERNEL
265331Samw #include <stdio.h>
275331Samw #include <strings.h>
285331Samw #include <stdlib.h>
295331Samw #include <syslog.h>
305331Samw #include <smbsrv/libsmb.h>
315331Samw #else /* _KERNEL */
325331Samw #include <sys/types.h>
335331Samw #include <sys/sunddi.h>
345331Samw #endif /* _KERNEL */
355331Samw 
366432Sas200622 #include <smbsrv/smb_sid.h>
375331Samw 
386432Sas200622 static smb_sid_t *smb_sid_alloc(size_t);
395331Samw 
405331Samw /*
416432Sas200622  * smb_sid_isvalid
425331Samw  *
436432Sas200622  * Performs a minimal SID validation.
445331Samw  */
456432Sas200622 boolean_t
smb_sid_isvalid(smb_sid_t * sid)466432Sas200622 smb_sid_isvalid(smb_sid_t *sid)
475331Samw {
486432Sas200622 	if (sid == NULL)
496432Sas200622 		return (B_FALSE);
505331Samw 
516432Sas200622 	return ((sid->sid_revision == NT_SID_REVISION) &&
526432Sas200622 	    (sid->sid_subauthcnt < NT_SID_SUBAUTH_MAX));
535331Samw }
545331Samw 
555331Samw /*
566432Sas200622  * smb_sid_len
575331Samw  *
585331Samw  * Returns the number of bytes required to hold the sid.
595331Samw  */
605331Samw int
smb_sid_len(smb_sid_t * sid)616432Sas200622 smb_sid_len(smb_sid_t *sid)
625331Samw {
636432Sas200622 	if (sid == NULL)
645331Samw 		return (0);
655331Samw 
666432Sas200622 	return (sizeof (smb_sid_t) - sizeof (uint32_t)
676432Sas200622 	    + (sid->sid_subauthcnt * sizeof (uint32_t)));
685331Samw }
695331Samw 
705331Samw /*
716432Sas200622  * smb_sid_dup
725331Samw  *
736432Sas200622  * Make a duplicate of the specified sid. The memory for the new sid
746432Sas200622  * should be freed by calling smb_sid_free().
756432Sas200622  * A pointer to the new sid is returned.
765331Samw  */
776432Sas200622 smb_sid_t *
smb_sid_dup(smb_sid_t * sid)786432Sas200622 smb_sid_dup(smb_sid_t *sid)
795331Samw {
806432Sas200622 	smb_sid_t *new_sid;
815331Samw 	int size;
825331Samw 
836432Sas200622 	if (sid == NULL)
846432Sas200622 		return (NULL);
855331Samw 
866432Sas200622 	size = smb_sid_len(sid);
876432Sas200622 	if ((new_sid = smb_sid_alloc(size)) == NULL)
886432Sas200622 		return (NULL);
895331Samw 
906432Sas200622 	bcopy(sid, new_sid, size);
915331Samw 	return (new_sid);
925331Samw }
935331Samw 
945331Samw 
955331Samw /*
966432Sas200622  * smb_sid_splice
975331Samw  *
986432Sas200622  * Make a full sid from a domain sid and a relative id (rid).
996432Sas200622  * The memory for the result sid should be freed by calling
1006432Sas200622  * smb_sid_free(). A pointer to the new sid is returned.
1015331Samw  */
1026432Sas200622 smb_sid_t *
smb_sid_splice(smb_sid_t * domain_sid,uint32_t rid)1036432Sas200622 smb_sid_splice(smb_sid_t *domain_sid, uint32_t rid)
1045331Samw {
1056432Sas200622 	smb_sid_t *sid;
1065331Samw 	int size;
1075331Samw 
1086432Sas200622 	if (domain_sid == NULL)
1096432Sas200622 		return (NULL);
1105331Samw 
1116432Sas200622 	size = smb_sid_len(domain_sid);
1126432Sas200622 	if ((sid = smb_sid_alloc(size + sizeof (rid))) == NULL)
1136432Sas200622 		return (NULL);
1145331Samw 
1156432Sas200622 	bcopy(domain_sid, sid, size);
1165331Samw 
1176432Sas200622 	sid->sid_subauth[domain_sid->sid_subauthcnt] = rid;
1186432Sas200622 	++sid->sid_subauthcnt;
1196432Sas200622 
1205331Samw 	return (sid);
1215331Samw }
1225331Samw 
1235331Samw /*
1246432Sas200622  * smb_sid_getrid
1255331Samw  *
1266432Sas200622  * Return the Relative Id (RID) of the specified SID. It is the
1275331Samw  * caller's responsibility to ensure that this is an appropriate SID.
1285331Samw  * All we do here is return the last sub-authority from the SID.
1295331Samw  */
1305331Samw int
smb_sid_getrid(smb_sid_t * sid,uint32_t * rid)1316432Sas200622 smb_sid_getrid(smb_sid_t *sid, uint32_t *rid)
1325331Samw {
1336432Sas200622 	if (!smb_sid_isvalid(sid) || (rid == NULL) ||
1346432Sas200622 	    (sid->sid_subauthcnt == 0))
1355331Samw 		return (-1);
1365331Samw 
1376432Sas200622 	*rid = sid->sid_subauth[sid->sid_subauthcnt - 1];
1386432Sas200622 	return (0);
1396432Sas200622 }
1406432Sas200622 
1416432Sas200622 /*
1426432Sas200622  * smb_sid_split
1436432Sas200622  *
1446432Sas200622  * Take a full sid and split it into a domain sid and a relative id (rid).
1458670SJose.Borrego@Sun.COM  * The domain SID is allocated and a pointer to it will be returned. The
1468670SJose.Borrego@Sun.COM  * RID value is passed back in 'rid' arg if it's not NULL. The allocated
1478670SJose.Borrego@Sun.COM  * memory for the domain SID must be freed by caller.
1486432Sas200622  */
1498670SJose.Borrego@Sun.COM smb_sid_t *
smb_sid_split(smb_sid_t * sid,uint32_t * rid)1506432Sas200622 smb_sid_split(smb_sid_t *sid, uint32_t *rid)
1516432Sas200622 {
1528670SJose.Borrego@Sun.COM 	smb_sid_t *domsid;
1538670SJose.Borrego@Sun.COM 
1546432Sas200622 	if (!smb_sid_isvalid(sid) || (sid->sid_subauthcnt == 0))
1558670SJose.Borrego@Sun.COM 		return (NULL);
1565331Samw 
1578670SJose.Borrego@Sun.COM 	if ((domsid = smb_sid_dup(sid)) == NULL)
1588670SJose.Borrego@Sun.COM 		return (NULL);
1598670SJose.Borrego@Sun.COM 
1608670SJose.Borrego@Sun.COM 	--domsid->sid_subauthcnt;
1615331Samw 	if (rid)
1628670SJose.Borrego@Sun.COM 		*rid = domsid->sid_subauth[domsid->sid_subauthcnt];
1638670SJose.Borrego@Sun.COM 
1648670SJose.Borrego@Sun.COM 	return (domsid);
1655331Samw }
1665331Samw 
1675331Samw /*
1686432Sas200622  * smb_sid_splitstr
1695331Samw  *
1706432Sas200622  * Takes a full sid in string form and split it into a domain sid and a
1716432Sas200622  * relative id (rid).
1726432Sas200622  *
1736432Sas200622  * IMPORTANT: The original sid is modified in place. This function assumes
1746432Sas200622  * given SID is in valid string format.
1755331Samw  */
1765331Samw int
smb_sid_splitstr(char * strsid,uint32_t * rid)1776432Sas200622 smb_sid_splitstr(char *strsid, uint32_t *rid)
1785331Samw {
1796432Sas200622 	char *p;
1806432Sas200622 
1816432Sas200622 	if ((p = strrchr(strsid, '-')) == NULL)
1825331Samw 		return (-1);
1836432Sas200622 
1846432Sas200622 	*p++ = '\0';
1856432Sas200622 	if (rid) {
1866432Sas200622 #ifdef _KERNEL
1876432Sas200622 		unsigned long sua = 0;
1886432Sas200622 		(void) ddi_strtoul(p, NULL, 10, &sua);
1896432Sas200622 		*rid = (uint32_t)sua;
1906432Sas200622 #else
1916432Sas200622 		*rid = strtoul(p, NULL, 10);
1926432Sas200622 #endif
1935331Samw 	}
1945331Samw 
1955331Samw 	return (0);
1965331Samw }
1975331Samw 
1985331Samw /*
1996432Sas200622  * smb_sid_cmp
2005331Samw  *
2015331Samw  * Compare two SIDs and return a boolean result. The checks are ordered
2025331Samw  * such that components that are more likely to differ are checked
2035331Samw  * first. For example, after checking that the SIDs contain the same
2046432Sas200622  * sid_subauthcnt, we check the sub-authorities in reverse order because
2055331Samw  * the RID is the most likely differentiator between two SIDs, i.e.
2065331Samw  * they are probably going to be in the same domain.
2075331Samw  */
2086432Sas200622 boolean_t
smb_sid_cmp(smb_sid_t * sid1,smb_sid_t * sid2)2096432Sas200622 smb_sid_cmp(smb_sid_t *sid1, smb_sid_t *sid2)
2105331Samw {
2115331Samw 	int i;
2125331Samw 
2136432Sas200622 	if (sid1 == NULL || sid2 == NULL)
2146432Sas200622 		return (B_FALSE);
2155331Samw 
2166432Sas200622 	if (sid1->sid_subauthcnt != sid2->sid_subauthcnt ||
2176432Sas200622 	    sid1->sid_revision != sid2->sid_revision)
2186432Sas200622 		return (B_FALSE);
2195331Samw 
2206432Sas200622 	for (i = sid1->sid_subauthcnt - 1; i >= 0; --i)
2216432Sas200622 		if (sid1->sid_subauth[i] != sid2->sid_subauth[i])
2226432Sas200622 			return (B_FALSE);
2235331Samw 
2246432Sas200622 	if (bcmp(&sid1->sid_authority, &sid2->sid_authority, NT_SID_AUTH_MAX))
2256432Sas200622 		return (B_FALSE);
2265331Samw 
2276432Sas200622 	return (B_TRUE);
2285331Samw }
2295331Samw 
2305331Samw /*
2316432Sas200622  * smb_sid_indomain
2325331Samw  *
2335331Samw  * Check if given SID is in given domain.
2345331Samw  */
2356432Sas200622 boolean_t
smb_sid_indomain(smb_sid_t * domain_sid,smb_sid_t * sid)2366432Sas200622 smb_sid_indomain(smb_sid_t *domain_sid, smb_sid_t *sid)
2375331Samw {
2385331Samw 	int i;
2395331Samw 
2406432Sas200622 	if (sid == NULL || domain_sid == NULL)
2416432Sas200622 		return (B_FALSE);
2425331Samw 
2436432Sas200622 	if (domain_sid->sid_revision != sid->sid_revision ||
2446432Sas200622 	    sid->sid_subauthcnt < domain_sid->sid_subauthcnt)
2456432Sas200622 		return (B_FALSE);
2465331Samw 
2476432Sas200622 	for (i = domain_sid->sid_subauthcnt - 1; i >= 0; --i)
2486432Sas200622 		if (domain_sid->sid_subauth[i] != sid->sid_subauth[i])
2496432Sas200622 			return (B_FALSE);
2505331Samw 
2516432Sas200622 	if (bcmp(&domain_sid->sid_authority, &sid->sid_authority,
2526432Sas200622 	    NT_SID_AUTH_MAX))
2536432Sas200622 		return (B_FALSE);
2545331Samw 
2556432Sas200622 	return (B_TRUE);
2565331Samw }
2575331Samw 
2585331Samw #ifndef _KERNEL
2595331Samw /*
2606432Sas200622  * smb_sid_islocal
2615331Samw  *
2626432Sas200622  * Check a SID to see if it belongs to the local domain.
2635331Samw  */
2646432Sas200622 boolean_t
smb_sid_islocal(smb_sid_t * sid)2656432Sas200622 smb_sid_islocal(smb_sid_t *sid)
2665331Samw {
26710717Samw@Sun.COM 	smb_domain_t di;
2689832Samw@Sun.COM 	boolean_t islocal = B_FALSE;
2699832Samw@Sun.COM 
27010717Samw@Sun.COM 	if (smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di))
2719832Samw@Sun.COM 		islocal = smb_sid_indomain(di.di_binsid, sid);
2729832Samw@Sun.COM 
2739832Samw@Sun.COM 	return (islocal);
2745331Samw }
2755331Samw #endif /* _KERNEL */
2765331Samw 
2775331Samw /*
2786432Sas200622  * smb_sid_tostr
2795331Samw  *
2806432Sas200622  * Fill in the passed buffer with the string form of the given
2816432Sas200622  * binary sid.
2825331Samw  */
2835331Samw void
smb_sid_tostr(const smb_sid_t * sid,char * strsid)284*12508Samw@Sun.COM smb_sid_tostr(const smb_sid_t *sid, char *strsid)
2855331Samw {
2866432Sas200622 	char *p = strsid;
2876432Sas200622 	int i;
2885331Samw 
2896432Sas200622 	if (sid == NULL || strsid == NULL)
2905331Samw 		return;
2915331Samw 
2926432Sas200622 	(void) sprintf(p, "S-%d-", sid->sid_revision);
2935331Samw 	while (*p)
2946432Sas200622 		p++;
2955331Samw 
2965331Samw 	for (i = 0; i < NT_SID_AUTH_MAX; ++i) {
2976432Sas200622 		if (sid->sid_authority[i] != 0 || i == NT_SID_AUTH_MAX - 1) {
2986432Sas200622 			(void) sprintf(p, "%d", sid->sid_authority[i]);
2995331Samw 			while (*p)
3006432Sas200622 				p++;
3015331Samw 		}
3025331Samw 	}
3035331Samw 
3046432Sas200622 	for (i = 0; i < sid->sid_subauthcnt && i < NT_SID_SUBAUTH_MAX; ++i) {
3056432Sas200622 		(void) sprintf(p, "-%u", sid->sid_subauth[i]);
3065331Samw 		while (*p)
3076432Sas200622 			p++;
3085331Samw 	}
3095331Samw }
3105331Samw 
3115331Samw /*
3126432Sas200622  * smb_sid_fromstr
3135331Samw  *
3145331Samw  * Converts a SID in string form to a SID structure. There are lots of
3155331Samw  * simplifying assumptions in here. The memory for the SID is allocated
3165331Samw  * as if it was the largest possible SID; the caller is responsible for
3175331Samw  * freeing the memory when it is no longer required. We assume that the
3185331Samw  * string starts with "S-1-" and that the authority is held in the last
3195331Samw  * byte, which should be okay for most situations. It also assumes the
3205331Samw  * sub-authorities are in decimal format.
3215331Samw  *
3225331Samw  * On success, a pointer to a SID is returned. Otherwise a null pointer
3235331Samw  * is returned.
3245331Samw  */
3256432Sas200622 #ifdef _KERNEL
3266432Sas200622 smb_sid_t *
smb_sid_fromstr(const char * sidstr)327*12508Samw@Sun.COM smb_sid_fromstr(const char *sidstr)
3285331Samw {
3296432Sas200622 	smb_sid_t *sid;
3306432Sas200622 	smb_sid_t *retsid;
331*12508Samw@Sun.COM 	const char *p;
3325331Samw 	int size;
3336432Sas200622 	uint8_t i;
3346432Sas200622 	unsigned long sua;
3355331Samw 
3366432Sas200622 	if (sidstr == NULL)
3376432Sas200622 		return (NULL);
3385331Samw 
3396432Sas200622 	if (strncmp(sidstr, "S-1-", 4) != 0)
3406432Sas200622 		return (NULL);
3415331Samw 
3426432Sas200622 	size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t));
3436432Sas200622 	sid = kmem_zalloc(size, KM_SLEEP);
3446432Sas200622 
3456432Sas200622 	sid->sid_revision = NT_SID_REVISION;
3465331Samw 	sua = 0;
3476432Sas200622 	(void) ddi_strtoul(&sidstr[4], 0, 10, &sua);
3486432Sas200622 	sid->sid_authority[5] = (uint8_t)sua;
3495331Samw 
3505331Samw 	for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) {
3515331Samw 		while (*p && *p == '-')
3525331Samw 			++p;
3535331Samw 
3545331Samw 		if (*p < '0' || *p > '9') {
3556432Sas200622 			kmem_free(sid, size);
3566432Sas200622 			return (NULL);
3575331Samw 		}
3585331Samw 
3595331Samw 		sua = 0;
3606432Sas200622 		(void) ddi_strtoul(p, 0, 10, &sua);
3616432Sas200622 		sid->sid_subauth[i] = (uint32_t)sua;
3625331Samw 
3635331Samw 		while (*p && *p != '-')
3645331Samw 			++p;
3655331Samw 	}
3665331Samw 
3676432Sas200622 	sid->sid_subauthcnt = i;
3686432Sas200622 	retsid = smb_sid_dup(sid);
3696432Sas200622 	kmem_free(sid, size);
3706432Sas200622 
3716432Sas200622 	return (retsid);
3726432Sas200622 }
3736432Sas200622 #else /* _KERNEL */
3746432Sas200622 smb_sid_t *
smb_sid_fromstr(const char * sidstr)375*12508Samw@Sun.COM smb_sid_fromstr(const char *sidstr)
3766432Sas200622 {
3776432Sas200622 	smb_sid_t *sid;
378*12508Samw@Sun.COM 	const char *p;
3796432Sas200622 	int size;
3806432Sas200622 	uint8_t i;
3816432Sas200622 
3826432Sas200622 	if (sidstr == NULL)
3836432Sas200622 		return (NULL);
3846432Sas200622 
3856432Sas200622 	if (strncmp(sidstr, "S-1-", 4) != 0)
3866432Sas200622 		return (NULL);
3876432Sas200622 
3886432Sas200622 	size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t));
3896432Sas200622 
3906432Sas200622 	if ((sid = malloc(size)) == NULL)
3916432Sas200622 		return (NULL);
3926432Sas200622 
3936432Sas200622 	bzero(sid, size);
3946432Sas200622 	sid->sid_revision = NT_SID_REVISION;
3956432Sas200622 	sid->sid_authority[5] = atoi(&sidstr[4]);
3966432Sas200622 
3976432Sas200622 	for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) {
3986432Sas200622 		while (*p && *p == '-')
3996432Sas200622 			++p;
4006432Sas200622 
4016432Sas200622 		if (*p < '0' || *p > '9') {
4026432Sas200622 			free(sid);
4036432Sas200622 			return (NULL);
4046432Sas200622 		}
4056432Sas200622 
4066432Sas200622 		sid->sid_subauth[i] = strtoul(p, NULL, 10);
4076432Sas200622 
4086432Sas200622 		while (*p && *p != '-')
4096432Sas200622 			++p;
4106432Sas200622 	}
4116432Sas200622 
4126432Sas200622 	sid->sid_subauthcnt = i;
4135331Samw 	return (sid);
4145331Samw }
4156432Sas200622 #endif /* _KERNEL */
4165331Samw 
4175331Samw /*
4186432Sas200622  * smb_sid_type2str
4195331Samw  *
4205331Samw  * Returns the text name for a SID_NAME_USE value. The SID_NAME_USE
4215331Samw  * provides the context for a SID, i.e. the type of resource to which
4225331Samw  * it refers.
4235331Samw  */
4245331Samw char *
smb_sid_type2str(uint16_t snu_id)4256432Sas200622 smb_sid_type2str(uint16_t snu_id)
4265331Samw {
4275331Samw 	static char *snu_name[] = {
4285331Samw 		"SidTypeSidPrefix",
4295331Samw 		"SidTypeUser",
4305331Samw 		"SidTypeGroup",
4315331Samw 		"SidTypeDomain",
4325331Samw 		"SidTypeAlias",
4335331Samw 		"SidTypeWellKnownGroup",
4345331Samw 		"SidTypeDeletedAccount",
4355331Samw 		"SidTypeInvalid",
43611337SWilliam.Krier@Sun.COM 		"SidTypeUnknown",
43711337SWilliam.Krier@Sun.COM 		"SidTypeComputer",
43811337SWilliam.Krier@Sun.COM 		"SidTypeLabel"
4395331Samw 	};
4405331Samw 
4415331Samw 	if (snu_id < ((sizeof (snu_name)/sizeof (snu_name[0]))))
4425331Samw 		return (snu_name[snu_id]);
4436432Sas200622 
4446432Sas200622 	return (snu_name[SidTypeUnknown]);
4455331Samw }
4465331Samw 
4476432Sas200622 static smb_sid_t *
smb_sid_alloc(size_t size)4486432Sas200622 smb_sid_alloc(size_t size)
4495331Samw {
4506432Sas200622 	smb_sid_t *sid;
4516432Sas200622 #ifdef _KERNEL
4526432Sas200622 	sid = kmem_alloc(size, KM_SLEEP);
4536432Sas200622 #else
4546432Sas200622 	sid = malloc(size);
4556432Sas200622 #endif
4566432Sas200622 	return (sid);
4576432Sas200622 }
4585331Samw 
4596432Sas200622 void
smb_sid_free(smb_sid_t * sid)4606432Sas200622 smb_sid_free(smb_sid_t *sid)
4616432Sas200622 {
4626432Sas200622 #ifdef _KERNEL
4636432Sas200622 	if (sid == NULL)
4646432Sas200622 		return;
4655331Samw 
4666432Sas200622 	kmem_free(sid, smb_sid_len(sid));
4676432Sas200622 #else
4686432Sas200622 	free(sid);
4696432Sas200622 #endif
4705331Samw }
4718670SJose.Borrego@Sun.COM 
4728670SJose.Borrego@Sun.COM #ifndef _KERNEL
4738670SJose.Borrego@Sun.COM void
smb_ids_free(smb_ids_t * ids)4748670SJose.Borrego@Sun.COM smb_ids_free(smb_ids_t *ids)
4758670SJose.Borrego@Sun.COM {
4768670SJose.Borrego@Sun.COM 	smb_id_t *id;
4778670SJose.Borrego@Sun.COM 	int i;
4788670SJose.Borrego@Sun.COM 
4798670SJose.Borrego@Sun.COM 	if ((ids != NULL) && (ids->i_ids != NULL)) {
4808670SJose.Borrego@Sun.COM 		id = ids->i_ids;
4818670SJose.Borrego@Sun.COM 		for (i = 0; i < ids->i_cnt; i++, id++)
4828670SJose.Borrego@Sun.COM 			smb_sid_free(id->i_sid);
4838670SJose.Borrego@Sun.COM 
4848670SJose.Borrego@Sun.COM 		free(ids->i_ids);
4858670SJose.Borrego@Sun.COM 	}
4868670SJose.Borrego@Sun.COM }
4878670SJose.Borrego@Sun.COM #endif
488