xref: /illumos-gate/usr/src/common/smbsrv/smb_sid.c (revision a1d92fe4831144630aa0b262cde1629785c37f23)
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