Home | History | Annotate | Download | only in smbsrv
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 /*
     29  * NT Security Identifier (SID) library functions.
     30  */
     31 
     32 #ifndef _KERNEL
     33 #include <stdio.h>
     34 #include <strings.h>
     35 #include <stdlib.h>
     36 #include <syslog.h>
     37 #include <smbsrv/libsmb.h>
     38 #else /* _KERNEL */
     39 #include <sys/types.h>
     40 #include <sys/sunddi.h>
     41 #endif /* _KERNEL */
     42 
     43 #include <smbsrv/smb_sid.h>
     44 
     45 static smb_sid_t *smb_sid_alloc(size_t);
     46 
     47 /*
     48  * smb_sid_isvalid
     49  *
     50  * Performs a minimal SID validation.
     51  */
     52 boolean_t
     53 smb_sid_isvalid(smb_sid_t *sid)
     54 {
     55 	if (sid == NULL)
     56 		return (B_FALSE);
     57 
     58 	return ((sid->sid_revision == NT_SID_REVISION) &&
     59 	    (sid->sid_subauthcnt < NT_SID_SUBAUTH_MAX));
     60 }
     61 
     62 /*
     63  * smb_sid_len
     64  *
     65  * Returns the number of bytes required to hold the sid.
     66  */
     67 int
     68 smb_sid_len(smb_sid_t *sid)
     69 {
     70 	if (sid == NULL)
     71 		return (0);
     72 
     73 	return (sizeof (smb_sid_t) - sizeof (uint32_t)
     74 	    + (sid->sid_subauthcnt * sizeof (uint32_t)));
     75 }
     76 
     77 /*
     78  * smb_sid_dup
     79  *
     80  * Make a duplicate of the specified sid. The memory for the new sid
     81  * should be freed by calling smb_sid_free().
     82  * A pointer to the new sid is returned.
     83  */
     84 smb_sid_t *
     85 smb_sid_dup(smb_sid_t *sid)
     86 {
     87 	smb_sid_t *new_sid;
     88 	int size;
     89 
     90 	if (sid == NULL)
     91 		return (NULL);
     92 
     93 	size = smb_sid_len(sid);
     94 	if ((new_sid = smb_sid_alloc(size)) == NULL)
     95 		return (NULL);
     96 
     97 	bcopy(sid, new_sid, size);
     98 	return (new_sid);
     99 }
    100 
    101 
    102 /*
    103  * smb_sid_splice
    104  *
    105  * Make a full sid from a domain sid and a relative id (rid).
    106  * The memory for the result sid should be freed by calling
    107  * smb_sid_free(). A pointer to the new sid is returned.
    108  */
    109 smb_sid_t *
    110 smb_sid_splice(smb_sid_t *domain_sid, uint32_t rid)
    111 {
    112 	smb_sid_t *sid;
    113 	int size;
    114 
    115 	if (domain_sid == NULL)
    116 		return (NULL);
    117 
    118 	size = smb_sid_len(domain_sid);
    119 	if ((sid = smb_sid_alloc(size + sizeof (rid))) == NULL)
    120 		return (NULL);
    121 
    122 	bcopy(domain_sid, sid, size);
    123 
    124 	sid->sid_subauth[domain_sid->sid_subauthcnt] = rid;
    125 	++sid->sid_subauthcnt;
    126 
    127 	return (sid);
    128 }
    129 
    130 /*
    131  * smb_sid_getrid
    132  *
    133  * Return the Relative Id (RID) of the specified SID. It is the
    134  * caller's responsibility to ensure that this is an appropriate SID.
    135  * All we do here is return the last sub-authority from the SID.
    136  */
    137 int
    138 smb_sid_getrid(smb_sid_t *sid, uint32_t *rid)
    139 {
    140 	if (!smb_sid_isvalid(sid) || (rid == NULL) ||
    141 	    (sid->sid_subauthcnt == 0))
    142 		return (-1);
    143 
    144 	*rid = sid->sid_subauth[sid->sid_subauthcnt - 1];
    145 	return (0);
    146 }
    147 
    148 /*
    149  * smb_sid_split
    150  *
    151  * Take a full sid and split it into a domain sid and a relative id (rid).
    152  *
    153  * IMPORTANT: The original sid is modified in place - use smb_sid_dup before
    154  * calling this function to preserve the original SID. Be careful if you're
    155  * using this function in kernel code, after calling this function 'sid'
    156  * cannot be passed to smb_sid_free() because the size won't match the original
    157  * allocated size. Use smb_sid_ksplit() instead.
    158  */
    159 int
    160 smb_sid_split(smb_sid_t *sid, uint32_t *rid)
    161 {
    162 	if (!smb_sid_isvalid(sid) || (sid->sid_subauthcnt == 0))
    163 		return (-1);
    164 
    165 	--sid->sid_subauthcnt;
    166 	if (rid)
    167 		*rid = sid->sid_subauth[sid->sid_subauthcnt];
    168 	return (0);
    169 }
    170 
    171 /*
    172  * smb_sid_splitstr
    173  *
    174  * Takes a full sid in string form and split it into a domain sid and a
    175  * relative id (rid).
    176  *
    177  * IMPORTANT: The original sid is modified in place. This function assumes
    178  * given SID is in valid string format.
    179  */
    180 int
    181 smb_sid_splitstr(char *strsid, uint32_t *rid)
    182 {
    183 	char *p;
    184 
    185 	if ((p = strrchr(strsid, '-')) == NULL)
    186 		return (-1);
    187 
    188 	*p++ = '\0';
    189 	if (rid) {
    190 #ifdef _KERNEL
    191 		unsigned long sua = 0;
    192 		(void) ddi_strtoul(p, NULL, 10, &sua);
    193 		*rid = (uint32_t)sua;
    194 #else
    195 		*rid = strtoul(p, NULL, 10);
    196 #endif
    197 	}
    198 
    199 	return (0);
    200 }
    201 
    202 /*
    203  * smb_sid_cmp
    204  *
    205  * Compare two SIDs and return a boolean result. The checks are ordered
    206  * such that components that are more likely to differ are checked
    207  * first. For example, after checking that the SIDs contain the same
    208  * sid_subauthcnt, we check the sub-authorities in reverse order because
    209  * the RID is the most likely differentiator between two SIDs, i.e.
    210  * they are probably going to be in the same domain.
    211  */
    212 boolean_t
    213 smb_sid_cmp(smb_sid_t *sid1, smb_sid_t *sid2)
    214 {
    215 	int i;
    216 
    217 	if (sid1 == NULL || sid2 == NULL)
    218 		return (B_FALSE);
    219 
    220 	if (sid1->sid_subauthcnt != sid2->sid_subauthcnt ||
    221 	    sid1->sid_revision != sid2->sid_revision)
    222 		return (B_FALSE);
    223 
    224 	for (i = sid1->sid_subauthcnt - 1; i >= 0; --i)
    225 		if (sid1->sid_subauth[i] != sid2->sid_subauth[i])
    226 			return (B_FALSE);
    227 
    228 	if (bcmp(&sid1->sid_authority, &sid2->sid_authority, NT_SID_AUTH_MAX))
    229 		return (B_FALSE);
    230 
    231 	return (B_TRUE);
    232 }
    233 
    234 /*
    235  * smb_sid_indomain
    236  *
    237  * Check if given SID is in given domain.
    238  */
    239 boolean_t
    240 smb_sid_indomain(smb_sid_t *domain_sid, smb_sid_t *sid)
    241 {
    242 	int i;
    243 
    244 	if (sid == NULL || domain_sid == NULL)
    245 		return (B_FALSE);
    246 
    247 	if (domain_sid->sid_revision != sid->sid_revision ||
    248 	    sid->sid_subauthcnt < domain_sid->sid_subauthcnt)
    249 		return (B_FALSE);
    250 
    251 	for (i = domain_sid->sid_subauthcnt - 1; i >= 0; --i)
    252 		if (domain_sid->sid_subauth[i] != sid->sid_subauth[i])
    253 			return (B_FALSE);
    254 
    255 	if (bcmp(&domain_sid->sid_authority, &sid->sid_authority,
    256 	    NT_SID_AUTH_MAX))
    257 		return (B_FALSE);
    258 
    259 	return (B_TRUE);
    260 }
    261 
    262 #ifndef _KERNEL
    263 /*
    264  * smb_sid_islocal
    265  *
    266  * Check a SID to see if it belongs to the local domain.
    267  */
    268 boolean_t
    269 smb_sid_islocal(smb_sid_t *sid)
    270 {
    271 	return (smb_sid_indomain(nt_domain_local_sid(), sid));
    272 }
    273 #endif /* _KERNEL */
    274 
    275 /*
    276  * smb_sid_tostr
    277  *
    278  * Fill in the passed buffer with the string form of the given
    279  * binary sid.
    280  */
    281 void
    282 smb_sid_tostr(smb_sid_t *sid, char *strsid)
    283 {
    284 	char *p = strsid;
    285 	int i;
    286 
    287 	if (sid == NULL || strsid == NULL)
    288 		return;
    289 
    290 	(void) sprintf(p, "S-%d-", sid->sid_revision);
    291 	while (*p)
    292 		p++;
    293 
    294 	for (i = 0; i < NT_SID_AUTH_MAX; ++i) {
    295 		if (sid->sid_authority[i] != 0 || i == NT_SID_AUTH_MAX - 1) {
    296 			(void) sprintf(p, "%d", sid->sid_authority[i]);
    297 			while (*p)
    298 				p++;
    299 		}
    300 	}
    301 
    302 	for (i = 0; i < sid->sid_subauthcnt && i < NT_SID_SUBAUTH_MAX; ++i) {
    303 		(void) sprintf(p, "-%u", sid->sid_subauth[i]);
    304 		while (*p)
    305 			p++;
    306 	}
    307 }
    308 
    309 /*
    310  * smb_sid_fromstr
    311  *
    312  * Converts a SID in string form to a SID structure. There are lots of
    313  * simplifying assumptions in here. The memory for the SID is allocated
    314  * as if it was the largest possible SID; the caller is responsible for
    315  * freeing the memory when it is no longer required. We assume that the
    316  * string starts with "S-1-" and that the authority is held in the last
    317  * byte, which should be okay for most situations. It also assumes the
    318  * sub-authorities are in decimal format.
    319  *
    320  * On success, a pointer to a SID is returned. Otherwise a null pointer
    321  * is returned.
    322  */
    323 #ifdef _KERNEL
    324 smb_sid_t *
    325 smb_sid_fromstr(char *sidstr)
    326 {
    327 	smb_sid_t *sid;
    328 	smb_sid_t *retsid;
    329 	char *p;
    330 	int size;
    331 	uint8_t i;
    332 	unsigned long sua;
    333 
    334 	if (sidstr == NULL)
    335 		return (NULL);
    336 
    337 	if (strncmp(sidstr, "S-1-", 4) != 0)
    338 		return (NULL);
    339 
    340 	size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t));
    341 	sid = kmem_zalloc(size, KM_SLEEP);
    342 
    343 	sid->sid_revision = NT_SID_REVISION;
    344 	sua = 0;
    345 	(void) ddi_strtoul(&sidstr[4], 0, 10, &sua);
    346 	sid->sid_authority[5] = (uint8_t)sua;
    347 
    348 	for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) {
    349 		while (*p && *p == '-')
    350 			++p;
    351 
    352 		if (*p < '0' || *p > '9') {
    353 			kmem_free(sid, size);
    354 			return (NULL);
    355 		}
    356 
    357 		sua = 0;
    358 		(void) ddi_strtoul(p, 0, 10, &sua);
    359 		sid->sid_subauth[i] = (uint32_t)sua;
    360 
    361 		while (*p && *p != '-')
    362 			++p;
    363 	}
    364 
    365 	sid->sid_subauthcnt = i;
    366 	retsid = smb_sid_dup(sid);
    367 	kmem_free(sid, size);
    368 
    369 	return (retsid);
    370 }
    371 #else /* _KERNEL */
    372 smb_sid_t *
    373 smb_sid_fromstr(char *sidstr)
    374 {
    375 	smb_sid_t *sid;
    376 	char *p;
    377 	int size;
    378 	uint8_t i;
    379 
    380 	if (sidstr == NULL)
    381 		return (NULL);
    382 
    383 	if (strncmp(sidstr, "S-1-", 4) != 0)
    384 		return (NULL);
    385 
    386 	size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t));
    387 
    388 	if ((sid = malloc(size)) == NULL)
    389 		return (NULL);
    390 
    391 	bzero(sid, size);
    392 	sid->sid_revision = NT_SID_REVISION;
    393 	sid->sid_authority[5] = atoi(&sidstr[4]);
    394 
    395 	for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) {
    396 		while (*p && *p == '-')
    397 			++p;
    398 
    399 		if (*p < '0' || *p > '9') {
    400 			free(sid);
    401 			return (NULL);
    402 		}
    403 
    404 		sid->sid_subauth[i] = strtoul(p, NULL, 10);
    405 
    406 		while (*p && *p != '-')
    407 			++p;
    408 	}
    409 
    410 	sid->sid_subauthcnt = i;
    411 	return (sid);
    412 }
    413 #endif /* _KERNEL */
    414 
    415 /*
    416  * smb_sid_type2str
    417  *
    418  * Returns the text name for a SID_NAME_USE value. The SID_NAME_USE
    419  * provides the context for a SID, i.e. the type of resource to which
    420  * it refers.
    421  */
    422 char *
    423 smb_sid_type2str(uint16_t snu_id)
    424 {
    425 	static char *snu_name[] = {
    426 		"SidTypeSidPrefix",
    427 		"SidTypeUser",
    428 		"SidTypeGroup",
    429 		"SidTypeDomain",
    430 		"SidTypeAlias",
    431 		"SidTypeWellKnownGroup",
    432 		"SidTypeDeletedAccount",
    433 		"SidTypeInvalid",
    434 		"SidTypeUnknown"
    435 	};
    436 
    437 	if (snu_id < ((sizeof (snu_name)/sizeof (snu_name[0]))))
    438 		return (snu_name[snu_id]);
    439 
    440 	return (snu_name[SidTypeUnknown]);
    441 }
    442 
    443 static smb_sid_t *
    444 smb_sid_alloc(size_t size)
    445 {
    446 	smb_sid_t *sid;
    447 #ifdef _KERNEL
    448 	sid = kmem_alloc(size, KM_SLEEP);
    449 #else
    450 	sid = malloc(size);
    451 #endif
    452 	return (sid);
    453 }
    454 
    455 void
    456 smb_sid_free(smb_sid_t *sid)
    457 {
    458 #ifdef _KERNEL
    459 	if (sid == NULL)
    460 		return;
    461 
    462 	kmem_free(sid, smb_sid_len(sid));
    463 #else
    464 	free(sid);
    465 #endif
    466 }
    467