xref: /illumos-gate/usr/src/common/smbsrv/smb_sid.c (revision b6c3f786)
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 2007 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/alloc.h>
44 #include <smbsrv/ntsid.h>
45 #include <smbsrv/ntstatus.h>
46 #include <smbsrv/smbinfo.h>
47 
48 
49 /*
50  * nt_sid_is_valid
51  *
52  * Check that a sid is valid. The checking is minimal: check the pointer
53  * is valid and that the revision and sub-authority count is legal.
54  * Returns 1 if the sid appears to be valid. Otherwise 0.
55  */
56 int
57 nt_sid_is_valid(nt_sid_t *sid)
58 {
59 	if (sid == 0)
60 		return (0);
61 
62 	return (sid->Revision == NT_SID_REVISION &&
63 	    sid->SubAuthCount < NT_SID_SUBAUTH_MAX) ? 1 : 0;
64 }
65 
66 
67 /*
68  * nt_sid_length
69  *
70  * Returns the number of bytes required to hold the sid.
71  */
72 int
73 nt_sid_length(nt_sid_t *sid)
74 {
75 	if (sid == 0)
76 		return (0);
77 
78 	return (sizeof (nt_sid_t) - sizeof (DWORD)
79 	    + (sid->SubAuthCount * sizeof (DWORD)));
80 }
81 
82 
83 /*
84  * nt_sid_dup
85  *
86  * Make a duplicate of the specified sid. The memory for the new sid is
87  * allocated using malloc so the caller should call free when it is no
88  * longer required. A pointer to the new sid is returned.
89  */
90 nt_sid_t *
91 nt_sid_dup(nt_sid_t *sid)
92 {
93 	nt_sid_t *new_sid;
94 	int size;
95 	int i;
96 
97 	if (sid == 0)
98 		return (0);
99 
100 	size = sizeof (nt_sid_t)
101 	    + (sid->SubAuthCount * sizeof (DWORD))
102 	    + sizeof (DWORD);
103 
104 	if ((new_sid = MEM_MALLOC("libnt", size)) == 0)
105 		return (0);
106 
107 	(void) memcpy(new_sid, sid, sizeof (nt_sid_t));
108 
109 	for (i = 0; i < sid->SubAuthCount && i < NT_SID_SUBAUTH_MAX; ++i)
110 		new_sid->SubAuthority[i] = sid->SubAuthority[i];
111 
112 	return (new_sid);
113 }
114 
115 
116 /*
117  * nt_sid_splice
118  *
119  * Make a full user sid from the domain sid and the user relative id
120  * (rid). The memory for the new sid is allocated using malloc so the
121  * caller should call free when it is no longer required. A pointer
122  * to the new sid is returned.
123  */
124 nt_sid_t *
125 nt_sid_splice(nt_sid_t *domain_sid, DWORD rid)
126 {
127 	nt_sid_t *sid;
128 	int size;
129 	int i;
130 
131 	if (domain_sid == 0)
132 		return (0);
133 
134 	size = sizeof (nt_sid_t)
135 	    + (domain_sid->SubAuthCount * sizeof (DWORD))
136 	    + sizeof (DWORD);
137 
138 	if ((sid = MEM_MALLOC("libnt", size)) == 0)
139 		return (0);
140 
141 	(void) memcpy(sid, domain_sid, sizeof (nt_sid_t));
142 
143 	for (i = 0; i < sid->SubAuthCount && i < NT_SID_SUBAUTH_MAX; ++i)
144 		sid->SubAuthority[i] = domain_sid->SubAuthority[i];
145 
146 	sid->SubAuthority[i] = rid;
147 	++sid->SubAuthCount;
148 	return (sid);
149 }
150 
151 
152 /*
153  * nt_sid_get_rid
154  *
155  * Return the Relative Id (RID) from the specified SID. It is the
156  * caller's responsibility to ensure that this is an appropriate SID.
157  * All we do here is return the last sub-authority from the SID.
158  */
159 int
160 nt_sid_get_rid(nt_sid_t *sid, DWORD *rid)
161 {
162 	if (!nt_sid_is_valid(sid))
163 		return (-1);
164 
165 	if (sid->SubAuthCount == 0) {
166 		return (-1);
167 	}
168 
169 	if (rid)
170 		*rid = sid->SubAuthority[sid->SubAuthCount - 1];
171 	return (0);
172 }
173 
174 
175 /*
176  * nt_sid_split
177  *
178  * Take a full user sid and split it into the domain sid and the user
179  * relative id (rid). The original sid is modified in place - use
180  * nt_sid_dup before calling this function to preserve the original SID.
181  */
182 int
183 nt_sid_split(nt_sid_t *sid, DWORD *rid)
184 {
185 	if (!nt_sid_is_valid(sid)) {
186 		return (-1);
187 	}
188 
189 	if (sid->SubAuthCount == 0) {
190 		return (-1);
191 	}
192 
193 	--sid->SubAuthCount;
194 	if (rid)
195 		*rid = sid->SubAuthority[sid->SubAuthCount];
196 	return (0);
197 }
198 
199 
200 /*
201  * nt_sid_gen_null_sid
202  *
203  * This function allocates a SID structure and initializes it as the
204  * well-known Null SID (S-1-0-0). A pointer to the SID is returned.
205  * As the memory for this structure is obtained via malloc, it is the
206  * caller's responsibility to free the memory when it is no longer
207  * required. If malloc fails, a null pointer is returned.
208  */
209 nt_sid_t *
210 nt_sid_gen_null_sid(void)
211 {
212 	nt_sid_t *sid;
213 	int size;
214 
215 	size = sizeof (nt_sid_t) + sizeof (DWORD);
216 
217 	if ((sid = MEM_MALLOC("libnt", size)) == 0) {
218 		return (0);
219 	}
220 
221 	sid->Revision = 1;
222 	sid->SubAuthCount = 1;
223 	return (sid);
224 }
225 
226 
227 /*
228  * nt_sid_is_equal
229  *
230  * Compare two SIDs and return a boolean result. The checks are ordered
231  * such that components that are more likely to differ are checked
232  * first. For example, after checking that the SIDs contain the same
233  * SubAuthCount, we check the sub-authorities in reverse order because
234  * the RID is the most likely differentiator between two SIDs, i.e.
235  * they are probably going to be in the same domain.
236  *
237  * Returns 1 if the SIDs are equal. Otherwise returns 0.
238  */
239 int
240 nt_sid_is_equal(nt_sid_t *sid1, nt_sid_t *sid2)
241 {
242 	int i;
243 
244 	if (sid1 == 0 || sid2 == 0)
245 		return (0);
246 
247 	if (sid1->SubAuthCount != sid2->SubAuthCount ||
248 	    sid1->Revision != sid2->Revision)
249 		return (0);
250 
251 	for (i = sid1->SubAuthCount - 1; i >= 0; --i)
252 		if (sid1->SubAuthority[i] != sid2->SubAuthority[i])
253 			return (0);
254 
255 	if (bcmp(&sid1->Authority, &sid2->Authority, NT_SID_AUTH_MAX))
256 		return (0);
257 
258 	return (1);
259 }
260 
261 /*
262  * nt_sid_is_indomain
263  *
264  * Check if given SID is in given domain.
265  * Returns 1 on success. Otherwise returns 0.
266  */
267 int
268 nt_sid_is_indomain(nt_sid_t *domain_sid, nt_sid_t *sid)
269 {
270 	int i;
271 
272 	if (sid == 0 || domain_sid == 0) {
273 		return (0);
274 	}
275 
276 	if (domain_sid->Revision != sid->Revision ||
277 	    sid->SubAuthCount < domain_sid->SubAuthCount)
278 		return (0);
279 
280 	for (i = domain_sid->SubAuthCount - 1; i >= 0; --i)
281 		if (domain_sid->SubAuthority[i] != sid->SubAuthority[i])
282 			return (0);
283 
284 	if (bcmp(&domain_sid->Authority, &sid->Authority, NT_SID_AUTH_MAX))
285 		return (0);
286 
287 	return (1);
288 }
289 
290 #ifndef _KERNEL
291 /*
292  * nt_sid_is_local
293  *
294  * Check a SID to see if it belongs to the local domain. This is almost
295  * the same as checking that two SIDs are equal except that we don't
296  * care if the specified SID contains extra sub-authorities. We're only
297  * interested in the domain part.
298  *
299  * Returns 1 if the SIDs are equal. Otherwise returns 0.
300  */
301 int
302 nt_sid_is_local(nt_sid_t *sid)
303 {
304 	nt_sid_t *local_sid;
305 
306 	local_sid = nt_domain_local_sid();
307 	return (nt_sid_is_indomain(local_sid, sid));
308 }
309 
310 /*
311  * nt_sid_is_builtin
312  *
313  * Check a SID to see if it belongs to the builtin domain.
314  * Returns 1 if the SID is a builtin SID. Otherwise returns 0.
315  */
316 int
317 nt_sid_is_builtin(nt_sid_t *sid)
318 {
319 	nt_domain_t *domain;
320 
321 	domain = nt_domain_lookupbytype(NT_DOMAIN_BUILTIN);
322 	if (domain == 0)
323 		return (0);
324 	return (nt_sid_is_indomain(domain->sid, sid));
325 }
326 #endif /* _KERNEL */
327 
328 /*
329  * nt_sid_is_domain_equal
330  *
331  * Compare two SIDs's domain and return a boolean result.
332  *
333  * Returns 1 if the domain SID are the same. Otherwise returns 0.
334  */
335 int
336 nt_sid_is_domain_equal(nt_sid_t *pSid1, nt_sid_t *pSid2)
337 {
338 	int		i, n;
339 
340 	if (pSid1->Revision != pSid2->Revision)
341 		return (0);
342 
343 	if (pSid1->SubAuthCount != pSid2->SubAuthCount)
344 		return (0);
345 
346 	if (bcmp(pSid1->Authority, pSid2->Authority, NT_SID_AUTH_MAX) != 0)
347 		return (0);
348 
349 	n = pSid1->SubAuthCount;
350 
351 	n -= 1;		/* don't compare last SubAuthority[] (aka RID) */
352 
353 	for (i = 0; i < n; i++)
354 		if (pSid1->SubAuthority[i] != pSid2->SubAuthority[i])
355 			return (0);
356 
357 	return (1);
358 }
359 
360 /*
361  * nt_sid_logf
362  *
363  * Format a sid and write it to the system log. See nt_sid_format
364  * for format information.
365  */
366 void
367 nt_sid_logf(nt_sid_t *sid)
368 {
369 	char *s;
370 
371 	if ((s = nt_sid_format(sid)) == 0)
372 		return;
373 
374 	MEM_FREE("libnt", s);
375 }
376 
377 
378 /*
379  * nt_sid_format
380  *
381  * Format a sid and return it as a string. The memory for the string is
382  * allocated using malloc so the caller should call free when it is no
383  * longer required. A pointer to the string is returned.
384  */
385 char *
386 nt_sid_format(nt_sid_t *sid)
387 {
388 	int i;
389 	char *fmtbuf;
390 	char *p;
391 
392 	if (sid == 0)
393 		return (0);
394 
395 	if ((fmtbuf = MEM_MALLOC("libnt", NT_SID_FMTBUF_SIZE)) == 0)
396 		return (0);
397 
398 	p = fmtbuf;
399 	(void) sprintf(p, "S-%d-", sid->Revision);
400 	while (*p)
401 		++p;
402 
403 	for (i = 0; i < NT_SID_AUTH_MAX; ++i) {
404 		if (sid->Authority[i] != 0 || i == NT_SID_AUTH_MAX - 1)	{
405 			(void) sprintf(p, "%d", sid->Authority[i]);
406 			while (*p)
407 				++p;
408 		}
409 	}
410 
411 	for (i = 0; i < sid->SubAuthCount && i < NT_SID_SUBAUTH_MAX; ++i) {
412 		(void) sprintf(p, "-%u", sid->SubAuthority[i]);
413 		while (*p)
414 			++p;
415 	}
416 
417 	return (fmtbuf);
418 }
419 
420 /*
421  * nt_sid_format2
422  *
423  * Format a sid and return it in the passed buffer.
424  */
425 void
426 nt_sid_format2(nt_sid_t *sid, char *fmtbuf)
427 {
428 	int i;
429 	char *p;
430 
431 	if (sid == 0 || fmtbuf == 0)
432 		return;
433 
434 	p = fmtbuf;
435 	(void) sprintf(p, "S-%d-", sid->Revision);
436 	while (*p)
437 		++p;
438 
439 	for (i = 0; i < NT_SID_AUTH_MAX; ++i) {
440 		if (sid->Authority[i] != 0 || i == NT_SID_AUTH_MAX - 1) {
441 			(void) sprintf(p, "%d", sid->Authority[i]);
442 			while (*p)
443 				++p;
444 		}
445 	}
446 
447 	for (i = 0; i < sid->SubAuthCount && i < NT_SID_SUBAUTH_MAX; ++i) {
448 		(void) sprintf(p, "-%u", sid->SubAuthority[i]);
449 		while (*p)
450 			++p;
451 	}
452 }
453 
454 /*
455  * nt_sid_strtosid
456  *
457  * Converts a SID in string form to a SID structure. There are lots of
458  * simplifying assumptions in here. The memory for the SID is allocated
459  * as if it was the largest possible SID; the caller is responsible for
460  * freeing the memory when it is no longer required. We assume that the
461  * string starts with "S-1-" and that the authority is held in the last
462  * byte, which should be okay for most situations. It also assumes the
463  * sub-authorities are in decimal format.
464  *
465  * On success, a pointer to a SID is returned. Otherwise a null pointer
466  * is returned.
467  *
468  * XXX this function may have endian issues
469  */
470 nt_sid_t *
471 nt_sid_strtosid(char *sidstr)
472 {
473 	nt_sid_t *sid;
474 	char *p;
475 	int size;
476 	BYTE i;
477 #ifdef _KERNEL
478 	long sua;
479 #endif /* _KERNEL */
480 
481 	if (sidstr == 0) {
482 		return (0);
483 	}
484 
485 	if (strncmp(sidstr, "S-1-", 4) != 0) {
486 		return (0);
487 	}
488 
489 	size = sizeof (nt_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (DWORD));
490 
491 	if ((sid = MEM_MALLOC("libnt", size)) == 0) {
492 		return (0);
493 	}
494 
495 	bzero(sid, size);
496 	sid->Revision = NT_SID_REVISION;
497 #ifndef _KERNEL
498 	sid->Authority[5] = atoi(&sidstr[4]);
499 #else /* _KERNEL */
500 	sua = 0;
501 	/* XXX Why are we treating sua as a long/unsigned long? */
502 	(void) ddi_strtoul(&sidstr[4], 0, 10, (unsigned long *)&sua);
503 	sid->Authority[5] = (BYTE)sua;
504 #endif /* _KERNEL */
505 
506 	for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) {
507 		while (*p && *p == '-')
508 			++p;
509 
510 		if (*p < '0' || *p > '9') {
511 			MEM_FREE("libnt", sid);
512 			return (0);
513 		}
514 
515 #ifndef _KERNEL
516 		sid->SubAuthority[i] = strtoul(p, 0, 10);
517 #else /* _KERNEL */
518 		sua = 0;
519 		(void) ddi_strtoul(p, 0, 10, (unsigned long *)&sua);
520 		sid->SubAuthority[i] = (DWORD)sua;
521 #endif /* _KERNEL */
522 
523 		while (*p && *p != '-')
524 			++p;
525 	}
526 
527 	sid->SubAuthCount = i;
528 	return (sid);
529 }
530 
531 
532 /*
533  * nt_sid_name_use
534  *
535  * Returns the text name for a SID_NAME_USE value. The SID_NAME_USE
536  * provides the context for a SID, i.e. the type of resource to which
537  * it refers.
538  */
539 char *
540 nt_sid_name_use(unsigned int snu_id)
541 {
542 	static char *snu_name[] = {
543 		"SidTypeSidPrefix",
544 		"SidTypeUser",
545 		"SidTypeGroup",
546 		"SidTypeDomain",
547 		"SidTypeAlias",
548 		"SidTypeWellKnownGroup",
549 		"SidTypeDeletedAccount",
550 		"SidTypeInvalid",
551 		"SidTypeUnknown"
552 	};
553 
554 	if (snu_id < ((sizeof (snu_name)/sizeof (snu_name[0]))))
555 		return (snu_name[snu_id]);
556 	else {
557 		return (snu_name[SidTypeUnknown]);
558 	}
559 }
560 
561 
562 /*
563  * nt_sid_copy
564  *
565  * Copy information of srcsid to dessid. The buffer should be allocated
566  * for dessid before passing to this function. The size of buffer for
567  * dessid should be specified in the buflen.
568  *
569  * Returns total bytes of information copied. If there is an error, 0
570  * will be returned.
571  */
572 int
573 nt_sid_copy(nt_sid_t *dessid, nt_sid_t *srcsid, unsigned buflen)
574 {
575 	unsigned		n_bytes;
576 
577 	if (!dessid || !srcsid)
578 		return (0);
579 
580 	n_bytes = nt_sid_length(srcsid);
581 	if (n_bytes > buflen)
582 		return (0);
583 
584 	bcopy((char *)srcsid, (char *)dessid, n_bytes);
585 
586 	return (n_bytes);
587 }
588