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