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