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