xref: /illumos-gate/usr/src/common/smbsrv/smb_sid.c (revision d9be5d44a919e9dbfe9d1e3e7a5557d9208b1de7)
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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2022 Tintri by DDN, Inc. All rights reserved.
24  */
25 
26 #if !defined(_KERNEL) && !defined(_FAKE_KERNEL)
27 #include <stdio.h>
28 #include <strings.h>
29 #include <stdlib.h>
30 #include <syslog.h>
31 #else	/* !_KERNEL && !_FAKE_KERNEL */
32 #include <sys/int_limits.h> /* Needed for _FAKE_KERNEL */
33 #include <sys/types.h>
34 #include <sys/systm.h>
35 #include <sys/sunddi.h>
36 #endif	/* !_KERNEL && !_FAKE_KERNEL */
37 
38 #include <smbsrv/smb_sid.h>
39 
40 static smb_sid_t *smb_sid_alloc(size_t);
41 
42 /*
43  * smb_sid_isvalid
44  *
45  * Performs a minimal SID validation.
46  */
47 boolean_t
smb_sid_isvalid(smb_sid_t * sid)48 smb_sid_isvalid(smb_sid_t *sid)
49 {
50 	if (sid == NULL)
51 		return (B_FALSE);
52 
53 	return ((sid->sid_revision == NT_SID_REVISION) &&
54 	    (sid->sid_subauthcnt <= NT_SID_SUBAUTH_MAX));
55 }
56 
57 /*
58  * smb_sid_len
59  *
60  * Returns the number of bytes required to hold the sid.
61  */
62 int
smb_sid_len(smb_sid_t * sid)63 smb_sid_len(smb_sid_t *sid)
64 {
65 	if (sid == NULL)
66 		return (0);
67 
68 	return (sizeof (smb_sid_t) - sizeof (uint32_t)
69 	    + (sid->sid_subauthcnt * sizeof (uint32_t)));
70 }
71 
72 /*
73  * smb_sid_dup
74  *
75  * Make a duplicate of the specified sid. The memory for the new sid
76  * should be freed by calling smb_sid_free().
77  * A pointer to the new sid is returned.
78  */
79 smb_sid_t *
smb_sid_dup(smb_sid_t * sid)80 smb_sid_dup(smb_sid_t *sid)
81 {
82 	smb_sid_t *new_sid;
83 	int size;
84 
85 	if (sid == NULL)
86 		return (NULL);
87 
88 	size = smb_sid_len(sid);
89 	if ((new_sid = smb_sid_alloc(size)) == NULL)
90 		return (NULL);
91 
92 	bcopy(sid, new_sid, size);
93 	return (new_sid);
94 }
95 
96 
97 /*
98  * smb_sid_splice
99  *
100  * Make a full sid from a domain sid and a relative id (rid).
101  * The memory for the result sid should be freed by calling
102  * smb_sid_free(). A pointer to the new sid is returned.
103  */
104 smb_sid_t *
smb_sid_splice(smb_sid_t * domain_sid,uint32_t rid)105 smb_sid_splice(smb_sid_t *domain_sid, uint32_t rid)
106 {
107 	smb_sid_t *sid;
108 	int size;
109 
110 	if (domain_sid == NULL)
111 		return (NULL);
112 
113 	size = smb_sid_len(domain_sid);
114 	if ((sid = smb_sid_alloc(size + sizeof (rid))) == NULL)
115 		return (NULL);
116 
117 	bcopy(domain_sid, sid, size);
118 
119 	sid->sid_subauth[domain_sid->sid_subauthcnt] = rid;
120 	++sid->sid_subauthcnt;
121 
122 	return (sid);
123 }
124 
125 /*
126  * smb_sid_getrid
127  *
128  * Return the Relative Id (RID) of the specified SID. It is the
129  * caller's responsibility to ensure that this is an appropriate SID.
130  * All we do here is return the last sub-authority from the SID.
131  */
132 int
smb_sid_getrid(smb_sid_t * sid,uint32_t * rid)133 smb_sid_getrid(smb_sid_t *sid, uint32_t *rid)
134 {
135 	if (!smb_sid_isvalid(sid) || (rid == NULL) ||
136 	    (sid->sid_subauthcnt == 0))
137 		return (-1);
138 
139 	*rid = sid->sid_subauth[sid->sid_subauthcnt - 1];
140 	return (0);
141 }
142 
143 /*
144  * smb_sid_split
145  *
146  * Take a full sid and split it into a domain sid and a relative id (rid).
147  * The domain SID is allocated and a pointer to it will be returned. The
148  * RID value is passed back in 'rid' arg if it's not NULL. The allocated
149  * memory for the domain SID must be freed by caller.
150  */
151 smb_sid_t *
smb_sid_split(smb_sid_t * sid,uint32_t * rid)152 smb_sid_split(smb_sid_t *sid, uint32_t *rid)
153 {
154 	smb_sid_t *domsid;
155 	int size;
156 
157 	if (!smb_sid_isvalid(sid) || (sid->sid_subauthcnt == 0))
158 		return (NULL);
159 
160 	/*
161 	 * We will reduce sid_subauthcnt by one, because
162 	 * the domain SID does not include the RID.
163 	 */
164 	size = smb_sid_len(sid) - sizeof (uint32_t);
165 	if ((domsid = smb_sid_alloc(size)) == NULL)
166 		return (NULL);
167 
168 	bcopy(sid, domsid, size);
169 	domsid->sid_subauthcnt = sid->sid_subauthcnt - 1;
170 
171 	if (rid)
172 		*rid = sid->sid_subauth[sid->sid_subauthcnt - 1];
173 
174 	return (domsid);
175 }
176 
177 /*
178  * smb_sid_splitstr
179  *
180  * Takes a full sid in string form and split it into a domain sid and a
181  * relative id (rid).
182  *
183  * IMPORTANT: The original sid is modified in place. This function assumes
184  * given SID is in valid string format.
185  */
186 int
smb_sid_splitstr(char * strsid,uint32_t * rid)187 smb_sid_splitstr(char *strsid, uint32_t *rid)
188 {
189 	char *p;
190 
191 	if ((p = strrchr(strsid, '-')) == NULL)
192 		return (-1);
193 
194 	*p++ = '\0';
195 	if (rid) {
196 #if defined(_KERNEL) || defined(_FAKE_KERNEL)
197 		unsigned long sua = 0;
198 		(void) ddi_strtoul(p, NULL, 10, &sua);
199 		*rid = (uint32_t)sua;
200 #else
201 		*rid = strtoul(p, NULL, 10);
202 #endif
203 	}
204 
205 	return (0);
206 }
207 
208 /*
209  * smb_sid_cmp
210  *
211  * Compare two SIDs and return a boolean result. The checks are ordered
212  * such that components that are more likely to differ are checked
213  * first. For example, after checking that the SIDs contain the same
214  * sid_subauthcnt, we check the sub-authorities in reverse order because
215  * the RID is the most likely differentiator between two SIDs, i.e.
216  * they are probably going to be in the same domain.
217  */
218 boolean_t
smb_sid_cmp(smb_sid_t * sid1,smb_sid_t * sid2)219 smb_sid_cmp(smb_sid_t *sid1, smb_sid_t *sid2)
220 {
221 	int i;
222 
223 	if (sid1 == NULL || sid2 == NULL)
224 		return (B_FALSE);
225 
226 	if (sid1->sid_subauthcnt != sid2->sid_subauthcnt ||
227 	    sid1->sid_revision != sid2->sid_revision)
228 		return (B_FALSE);
229 
230 	for (i = sid1->sid_subauthcnt - 1; i >= 0; --i)
231 		if (sid1->sid_subauth[i] != sid2->sid_subauth[i])
232 			return (B_FALSE);
233 
234 	if (bcmp(&sid1->sid_authority, &sid2->sid_authority, NT_SID_AUTH_MAX))
235 		return (B_FALSE);
236 
237 	return (B_TRUE);
238 }
239 
240 /*
241  * smb_sid_indomain
242  *
243  * Check if given SID is in given domain.
244  */
245 boolean_t
smb_sid_indomain(smb_sid_t * domain_sid,smb_sid_t * sid)246 smb_sid_indomain(smb_sid_t *domain_sid, smb_sid_t *sid)
247 {
248 	int i;
249 
250 	if (sid == NULL || domain_sid == NULL)
251 		return (B_FALSE);
252 
253 	if (domain_sid->sid_revision != sid->sid_revision ||
254 	    sid->sid_subauthcnt < domain_sid->sid_subauthcnt)
255 		return (B_FALSE);
256 
257 	for (i = domain_sid->sid_subauthcnt - 1; i >= 0; --i)
258 		if (domain_sid->sid_subauth[i] != sid->sid_subauth[i])
259 			return (B_FALSE);
260 
261 	if (bcmp(&domain_sid->sid_authority, &sid->sid_authority,
262 	    NT_SID_AUTH_MAX))
263 		return (B_FALSE);
264 
265 	return (B_TRUE);
266 }
267 
268 /*
269  * smb_sid_tostr
270  *
271  * Fill in the passed buffer with the string form of the given
272  * binary sid.
273  */
274 void
smb_sid_tostr(const smb_sid_t * sid,char * strsid)275 smb_sid_tostr(const smb_sid_t *sid, char *strsid)
276 {
277 	char *p = strsid;
278 	int i;
279 
280 	if (sid == NULL || strsid == NULL)
281 		return;
282 
283 	(void) sprintf(p, "S-%d-", sid->sid_revision);
284 	while (*p)
285 		p++;
286 
287 	for (i = 0; i < NT_SID_AUTH_MAX; ++i) {
288 		if (sid->sid_authority[i] != 0 || i == NT_SID_AUTH_MAX - 1) {
289 			(void) sprintf(p, "%d", sid->sid_authority[i]);
290 			while (*p)
291 				p++;
292 		}
293 	}
294 
295 	for (i = 0; i < sid->sid_subauthcnt && i < NT_SID_SUBAUTH_MAX; ++i) {
296 		(void) sprintf(p, "-%u", sid->sid_subauth[i]);
297 		while (*p)
298 			p++;
299 	}
300 }
301 
302 /*
303  * smb_sid_fromstr
304  *
305  * Converts a SID in string form to a SID structure. There are lots of
306  * simplifying assumptions in here. The memory for the SID is allocated
307  * as if it was the largest possible SID; the caller is responsible for
308  * freeing the memory when it is no longer required. We assume that the
309  * string starts with "S-1-" and that the authority is held in the last
310  * byte, which should be okay for most situations. It also assumes the
311  * sub-authorities are in decimal format.
312  *
313  * On success, a pointer to a SID is returned. Otherwise a null pointer
314  * is returned.
315  */
316 #if defined(_KERNEL) || defined(_FAKE_KERNEL)
317 smb_sid_t *
smb_sid_fromstr(const char * sidstr)318 smb_sid_fromstr(const char *sidstr)
319 {
320 	smb_sid_t *sid;
321 	smb_sid_t *retsid = NULL;
322 	const char *p;
323 	int size;
324 	uint8_t i;
325 	unsigned long sua;
326 
327 	if (sidstr == NULL)
328 		return (NULL);
329 
330 	if (strncmp(sidstr, "S-1-", 4) != 0)
331 		return (NULL);
332 
333 	sua = 0;
334 	(void) ddi_strtoul(&sidstr[4], (char **)&p, 10, &sua);
335 
336 	/*
337 	 * If ddi_strtoul() did the right thing, *p will point at the first '-'
338 	 * after the identifier authority.
339 	 * The IdentifierAuthority can be up to 2^48, but all known ones
340 	 * currently fit into a uint8_t.
341 	 * TODO: support IdentifierAuthorities > 255 (those over UINT32_MAX are
342 	 * hex-formatted).
343 	 */
344 	if (sua > UINT8_MAX || (*p != '-' && *p != '\0'))
345 		return (NULL);
346 
347 	size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t));
348 	sid = kmem_zalloc(size, KM_SLEEP);
349 	sid->sid_revision = NT_SID_REVISION;
350 	sid->sid_authority[5] = (uint8_t)sua;
351 
352 	for (i = 0; i < NT_SID_SUBAUTH_MAX && *p; ++i) {
353 		while (*p == '-')
354 			++p;
355 
356 		if (*p < '0' || *p > '9')
357 			goto out;
358 
359 		sua = 0;
360 		(void) ddi_strtoul(p, (char **)&p, 10, &sua);
361 		if (sua > UINT32_MAX)
362 			goto out;
363 		sid->sid_subauth[i] = (uint32_t)sua;
364 
365 		if (*p != '\0' && *p != '-')
366 			goto out;
367 	}
368 
369 	sid->sid_subauthcnt = i;
370 	retsid = smb_sid_dup(sid);
371 
372 out:
373 	kmem_free(sid, size);
374 	return (retsid);
375 }
376 #else /* _KERNEL */
377 smb_sid_t *
smb_sid_fromstr(const char * sidstr)378 smb_sid_fromstr(const char *sidstr)
379 {
380 	smb_sid_t *sid;
381 	const char *p;
382 	int size;
383 	uint8_t i;
384 	unsigned long sua;
385 
386 	if (sidstr == NULL)
387 		return (NULL);
388 
389 	if (strncmp(sidstr, "S-1-", 4) != 0)
390 		return (NULL);
391 
392 	sua = strtoul(&sidstr[4], (char **)&p, 10);
393 
394 	/*
395 	 * If strtoul() did the right thing, *p will point at the first '-'
396 	 * after the identifier authority.
397 	 * The IdentifierAuthority can be up to 2^48, but all known ones
398 	 * currently fit into a uint8_t.
399 	 * TODO: support IdentifierAuthorities > 255 (those over UINT32_MAX are
400 	 * hex-formatted).
401 	 */
402 	if (sua > UINT8_MAX || (*p != '-' && *p != '\0'))
403 		return (NULL);
404 
405 	size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t));
406 
407 	if ((sid = calloc(size, 1)) == NULL)
408 		return (NULL);
409 
410 	sid->sid_revision = NT_SID_REVISION;
411 	sid->sid_authority[5] = (uint8_t)sua;
412 
413 	for (i = 0; i < NT_SID_SUBAUTH_MAX && *p; ++i) {
414 		while (*p == '-')
415 			++p;
416 
417 		if (*p < '0' || *p > '9') {
418 			free(sid);
419 			return (NULL);
420 		}
421 
422 		sid->sid_subauth[i] = strtoul(p, (char **)&p, 10);
423 		if (*p != '\0' && *p != '-') {
424 			free(sid);
425 			return (NULL);
426 		}
427 	}
428 
429 	sid->sid_subauthcnt = i;
430 	return (sid);
431 }
432 #endif /* _KERNEL */
433 
434 /*
435  * smb_sid_type2str
436  *
437  * Returns the text name for a SID_NAME_USE value. The SID_NAME_USE
438  * provides the context for a SID, i.e. the type of resource to which
439  * it refers.
440  */
441 char *
smb_sid_type2str(uint16_t snu_id)442 smb_sid_type2str(uint16_t snu_id)
443 {
444 	static char *snu_name[] = {
445 		"SidTypeSidPrefix",
446 		"SidTypeUser",
447 		"SidTypeGroup",
448 		"SidTypeDomain",
449 		"SidTypeAlias",
450 		"SidTypeWellKnownGroup",
451 		"SidTypeDeletedAccount",
452 		"SidTypeInvalid",
453 		"SidTypeUnknown",
454 		"SidTypeComputer",
455 		"SidTypeLabel"
456 	};
457 
458 	if (snu_id < ((sizeof (snu_name)/sizeof (snu_name[0]))))
459 		return (snu_name[snu_id]);
460 
461 	return (snu_name[SidTypeUnknown]);
462 }
463 
464 static smb_sid_t *
smb_sid_alloc(size_t size)465 smb_sid_alloc(size_t size)
466 {
467 	smb_sid_t *sid;
468 #if defined(_KERNEL) || defined(_FAKE_KERNEL)
469 	sid = kmem_alloc(size, KM_SLEEP);
470 #else
471 	sid = malloc(size);
472 #endif
473 	return (sid);
474 }
475 
476 void
smb_sid_free(smb_sid_t * sid)477 smb_sid_free(smb_sid_t *sid)
478 {
479 #if defined(_KERNEL) || defined(_FAKE_KERNEL)
480 	if (sid == NULL)
481 		return;
482 
483 	kmem_free(sid, smb_sid_len(sid));
484 #else
485 	free(sid);
486 #endif
487 }
488