xref: /illumos-gate/usr/src/common/smbsrv/smb_sid.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
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
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
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 *
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 *
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
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 *
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 	/*
160 	 * We will reduce sid_subauthcnt by one, because
161 	 * the domain SID does not include the RID.
162 	 */
163 	size = smb_sid_len(sid) - sizeof (uint32_t);
164 	if ((domsid = smb_sid_alloc(size)) == NULL)
165 		return (NULL);
166 
167 	bcopy(sid, domsid, size);
168 	domsid->sid_subauthcnt = sid->sid_subauthcnt - 1;
169 
170 	if (rid)
171 		*rid = sid->sid_subauth[sid->sid_subauthcnt - 1];
172 
173 	return (domsid);
174 }
175 
176 /*
177  * smb_sid_splitstr
178  *
179  * Takes a full sid in string form and split it into a domain sid and a
180  * relative id (rid).
181  *
182  * IMPORTANT: The original sid is modified in place. This function assumes
183  * given SID is in valid string format.
184  */
185 int
186 smb_sid_splitstr(char *strsid, uint32_t *rid)
187 {
188 	char *p;
189 
190 	if ((p = strrchr(strsid, '-')) == NULL)
191 		return (-1);
192 
193 	*p++ = '\0';
194 	if (rid) {
195 #if defined(_KERNEL) || defined(_FAKE_KERNEL)
196 		unsigned long sua = 0;
197 		(void) ddi_strtoul(p, NULL, 10, &sua);
198 		*rid = (uint32_t)sua;
199 #else
200 		*rid = strtoul(p, NULL, 10);
201 #endif
202 	}
203 
204 	return (0);
205 }
206 
207 /*
208  * smb_sid_cmp
209  *
210  * Compare two SIDs and return a boolean result. The checks are ordered
211  * such that components that are more likely to differ are checked
212  * first. For example, after checking that the SIDs contain the same
213  * sid_subauthcnt, we check the sub-authorities in reverse order because
214  * the RID is the most likely differentiator between two SIDs, i.e.
215  * they are probably going to be in the same domain.
216  */
217 boolean_t
218 smb_sid_cmp(smb_sid_t *sid1, smb_sid_t *sid2)
219 {
220 	int i;
221 
222 	if (sid1 == NULL || sid2 == NULL)
223 		return (B_FALSE);
224 
225 	if (sid1->sid_subauthcnt != sid2->sid_subauthcnt ||
226 	    sid1->sid_revision != sid2->sid_revision)
227 		return (B_FALSE);
228 
229 	for (i = sid1->sid_subauthcnt - 1; i >= 0; --i)
230 		if (sid1->sid_subauth[i] != sid2->sid_subauth[i])
231 			return (B_FALSE);
232 
233 	if (bcmp(&sid1->sid_authority, &sid2->sid_authority, NT_SID_AUTH_MAX))
234 		return (B_FALSE);
235 
236 	return (B_TRUE);
237 }
238 
239 /*
240  * smb_sid_indomain
241  *
242  * Check if given SID is in given domain.
243  */
244 boolean_t
245 smb_sid_indomain(smb_sid_t *domain_sid, smb_sid_t *sid)
246 {
247 	int i;
248 
249 	if (sid == NULL || domain_sid == NULL)
250 		return (B_FALSE);
251 
252 	if (domain_sid->sid_revision != sid->sid_revision ||
253 	    sid->sid_subauthcnt < domain_sid->sid_subauthcnt)
254 		return (B_FALSE);
255 
256 	for (i = domain_sid->sid_subauthcnt - 1; i >= 0; --i)
257 		if (domain_sid->sid_subauth[i] != sid->sid_subauth[i])
258 			return (B_FALSE);
259 
260 	if (bcmp(&domain_sid->sid_authority, &sid->sid_authority,
261 	    NT_SID_AUTH_MAX))
262 		return (B_FALSE);
263 
264 	return (B_TRUE);
265 }
266 
267 /*
268  * smb_sid_tostr
269  *
270  * Fill in the passed buffer with the string form of the given
271  * binary sid.
272  */
273 void
274 smb_sid_tostr(const smb_sid_t *sid, char *strsid)
275 {
276 	char *p = strsid;
277 	int i;
278 
279 	if (sid == NULL || strsid == NULL)
280 		return;
281 
282 	(void) sprintf(p, "S-%d-", sid->sid_revision);
283 	while (*p)
284 		p++;
285 
286 	for (i = 0; i < NT_SID_AUTH_MAX; ++i) {
287 		if (sid->sid_authority[i] != 0 || i == NT_SID_AUTH_MAX - 1) {
288 			(void) sprintf(p, "%d", sid->sid_authority[i]);
289 			while (*p)
290 				p++;
291 		}
292 	}
293 
294 	for (i = 0; i < sid->sid_subauthcnt && i < NT_SID_SUBAUTH_MAX; ++i) {
295 		(void) sprintf(p, "-%u", sid->sid_subauth[i]);
296 		while (*p)
297 			p++;
298 	}
299 }
300 
301 /*
302  * smb_sid_fromstr
303  *
304  * Converts a SID in string form to a SID structure. There are lots of
305  * simplifying assumptions in here. The memory for the SID is allocated
306  * as if it was the largest possible SID; the caller is responsible for
307  * freeing the memory when it is no longer required. We assume that the
308  * string starts with "S-1-" and that the authority is held in the last
309  * byte, which should be okay for most situations. It also assumes the
310  * sub-authorities are in decimal format.
311  *
312  * On success, a pointer to a SID is returned. Otherwise a null pointer
313  * is returned.
314  */
315 #if defined(_KERNEL) || defined(_FAKE_KERNEL)
316 smb_sid_t *
317 smb_sid_fromstr(const char *sidstr)
318 {
319 	smb_sid_t *sid;
320 	smb_sid_t *retsid;
321 	const char *p;
322 	int size;
323 	uint8_t i;
324 	unsigned long sua;
325 
326 	if (sidstr == NULL)
327 		return (NULL);
328 
329 	if (strncmp(sidstr, "S-1-", 4) != 0)
330 		return (NULL);
331 
332 	size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t));
333 	sid = kmem_zalloc(size, KM_SLEEP);
334 
335 	sid->sid_revision = NT_SID_REVISION;
336 	sua = 0;
337 	(void) ddi_strtoul(&sidstr[4], 0, 10, &sua);
338 	sid->sid_authority[5] = (uint8_t)sua;
339 
340 	for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) {
341 		while (*p && *p == '-')
342 			++p;
343 
344 		if (*p < '0' || *p > '9') {
345 			kmem_free(sid, size);
346 			return (NULL);
347 		}
348 
349 		sua = 0;
350 		(void) ddi_strtoul(p, 0, 10, &sua);
351 		sid->sid_subauth[i] = (uint32_t)sua;
352 
353 		while (*p && *p != '-')
354 			++p;
355 	}
356 
357 	sid->sid_subauthcnt = i;
358 	retsid = smb_sid_dup(sid);
359 	kmem_free(sid, size);
360 
361 	return (retsid);
362 }
363 #else /* _KERNEL */
364 smb_sid_t *
365 smb_sid_fromstr(const char *sidstr)
366 {
367 	smb_sid_t *sid;
368 	const char *p;
369 	int size;
370 	uint8_t i;
371 
372 	if (sidstr == NULL)
373 		return (NULL);
374 
375 	if (strncmp(sidstr, "S-1-", 4) != 0)
376 		return (NULL);
377 
378 	size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t));
379 
380 	if ((sid = malloc(size)) == NULL)
381 		return (NULL);
382 
383 	bzero(sid, size);
384 	sid->sid_revision = NT_SID_REVISION;
385 	sid->sid_authority[5] = atoi(&sidstr[4]);
386 
387 	for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) {
388 		while (*p && *p == '-')
389 			++p;
390 
391 		if (*p < '0' || *p > '9') {
392 			free(sid);
393 			return (NULL);
394 		}
395 
396 		sid->sid_subauth[i] = strtoul(p, NULL, 10);
397 
398 		while (*p && *p != '-')
399 			++p;
400 	}
401 
402 	sid->sid_subauthcnt = i;
403 	return (sid);
404 }
405 #endif /* _KERNEL */
406 
407 /*
408  * smb_sid_type2str
409  *
410  * Returns the text name for a SID_NAME_USE value. The SID_NAME_USE
411  * provides the context for a SID, i.e. the type of resource to which
412  * it refers.
413  */
414 char *
415 smb_sid_type2str(uint16_t snu_id)
416 {
417 	static char *snu_name[] = {
418 		"SidTypeSidPrefix",
419 		"SidTypeUser",
420 		"SidTypeGroup",
421 		"SidTypeDomain",
422 		"SidTypeAlias",
423 		"SidTypeWellKnownGroup",
424 		"SidTypeDeletedAccount",
425 		"SidTypeInvalid",
426 		"SidTypeUnknown",
427 		"SidTypeComputer",
428 		"SidTypeLabel"
429 	};
430 
431 	if (snu_id < ((sizeof (snu_name)/sizeof (snu_name[0]))))
432 		return (snu_name[snu_id]);
433 
434 	return (snu_name[SidTypeUnknown]);
435 }
436 
437 static smb_sid_t *
438 smb_sid_alloc(size_t size)
439 {
440 	smb_sid_t *sid;
441 #if defined(_KERNEL) || defined(_FAKE_KERNEL)
442 	sid = kmem_alloc(size, KM_SLEEP);
443 #else
444 	sid = malloc(size);
445 #endif
446 	return (sid);
447 }
448 
449 void
450 smb_sid_free(smb_sid_t *sid)
451 {
452 #if defined(_KERNEL) || defined(_FAKE_KERNEL)
453 	if (sid == NULL)
454 		return;
455 
456 	kmem_free(sid, smb_sid_len(sid));
457 #else
458 	free(sid);
459 #endif
460 }
461