/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#if !defined(_KERNEL) && !defined(_FAKE_KERNEL)
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
#include <syslog.h>
#else /* !_KERNEL && !_FAKE_KERNEL */
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/sunddi.h>
#endif /* !_KERNEL && !_FAKE_KERNEL */
#include <smbsrv/smb_sid.h>
static smb_sid_t *smb_sid_alloc(size_t);
/*
* smb_sid_isvalid
*
* Performs a minimal SID validation.
*/
boolean_t
smb_sid_isvalid(smb_sid_t *sid)
{
if (sid == NULL)
return (B_FALSE);
return ((sid->sid_revision == NT_SID_REVISION) &&
(sid->sid_subauthcnt < NT_SID_SUBAUTH_MAX));
}
/*
* smb_sid_len
*
* Returns the number of bytes required to hold the sid.
*/
int
smb_sid_len(smb_sid_t *sid)
{
if (sid == NULL)
return (0);
return (sizeof (smb_sid_t) - sizeof (uint32_t)
+ (sid->sid_subauthcnt * sizeof (uint32_t)));
}
/*
* smb_sid_dup
*
* Make a duplicate of the specified sid. The memory for the new sid
* should be freed by calling smb_sid_free().
* A pointer to the new sid is returned.
*/
smb_sid_t *
smb_sid_dup(smb_sid_t *sid)
{
smb_sid_t *new_sid;
int size;
if (sid == NULL)
return (NULL);
size = smb_sid_len(sid);
if ((new_sid = smb_sid_alloc(size)) == NULL)
return (NULL);
bcopy(sid, new_sid, size);
return (new_sid);
}
/*
* smb_sid_splice
*
* Make a full sid from a domain sid and a relative id (rid).
* The memory for the result sid should be freed by calling
* smb_sid_free(). A pointer to the new sid is returned.
*/
smb_sid_t *
smb_sid_splice(smb_sid_t *domain_sid, uint32_t rid)
{
smb_sid_t *sid;
int size;
if (domain_sid == NULL)
return (NULL);
size = smb_sid_len(domain_sid);
if ((sid = smb_sid_alloc(size + sizeof (rid))) == NULL)
return (NULL);
bcopy(domain_sid, sid, size);
sid->sid_subauth[domain_sid->sid_subauthcnt] = rid;
++sid->sid_subauthcnt;
return (sid);
}
/*
* smb_sid_getrid
*
* Return the Relative Id (RID) of the specified SID. It is the
* caller's responsibility to ensure that this is an appropriate SID.
* All we do here is return the last sub-authority from the SID.
*/
int
smb_sid_getrid(smb_sid_t *sid, uint32_t *rid)
{
if (!smb_sid_isvalid(sid) || (rid == NULL) ||
(sid->sid_subauthcnt == 0))
return (-1);
*rid = sid->sid_subauth[sid->sid_subauthcnt - 1];
return (0);
}
/*
* smb_sid_split
*
* Take a full sid and split it into a domain sid and a relative id (rid).
* The domain SID is allocated and a pointer to it will be returned. The
* RID value is passed back in 'rid' arg if it's not NULL. The allocated
* memory for the domain SID must be freed by caller.
*/
smb_sid_t *
smb_sid_split(smb_sid_t *sid, uint32_t *rid)
{
smb_sid_t *domsid;
int size;
if (!smb_sid_isvalid(sid) || (sid->sid_subauthcnt == 0))
return (NULL);
/*
* We will reduce sid_subauthcnt by one, because
* the domain SID does not include the RID.
*/
size = smb_sid_len(sid) - sizeof (uint32_t);
if ((domsid = smb_sid_alloc(size)) == NULL)
return (NULL);
bcopy(sid, domsid, size);
domsid->sid_subauthcnt = sid->sid_subauthcnt - 1;
if (rid)
*rid = sid->sid_subauth[sid->sid_subauthcnt - 1];
return (domsid);
}
/*
* smb_sid_splitstr
*
* Takes a full sid in string form and split it into a domain sid and a
* relative id (rid).
*
* IMPORTANT: The original sid is modified in place. This function assumes
* given SID is in valid string format.
*/
int
smb_sid_splitstr(char *strsid, uint32_t *rid)
{
char *p;
if ((p = strrchr(strsid, '-')) == NULL)
return (-1);
*p++ = '\0';
if (rid) {
#if defined(_KERNEL) || defined(_FAKE_KERNEL)
unsigned long sua = 0;
(void) ddi_strtoul(p, NULL, 10, &sua);
*rid = (uint32_t)sua;
#else
*rid = strtoul(p, NULL, 10);
#endif
}
return (0);
}
/*
* smb_sid_cmp
*
* Compare two SIDs and return a boolean result. The checks are ordered
* such that components that are more likely to differ are checked
* first. For example, after checking that the SIDs contain the same
* sid_subauthcnt, we check the sub-authorities in reverse order because
* the RID is the most likely differentiator between two SIDs, i.e.
* they are probably going to be in the same domain.
*/
boolean_t
smb_sid_cmp(smb_sid_t *sid1, smb_sid_t *sid2)
{
int i;
if (sid1 == NULL || sid2 == NULL)
return (B_FALSE);
if (sid1->sid_subauthcnt != sid2->sid_subauthcnt ||
sid1->sid_revision != sid2->sid_revision)
return (B_FALSE);
for (i = sid1->sid_subauthcnt - 1; i >= 0; --i)
if (sid1->sid_subauth[i] != sid2->sid_subauth[i])
return (B_FALSE);
if (bcmp(&sid1->sid_authority, &sid2->sid_authority, NT_SID_AUTH_MAX))
return (B_FALSE);
return (B_TRUE);
}
/*
* smb_sid_indomain
*
* Check if given SID is in given domain.
*/
boolean_t
smb_sid_indomain(smb_sid_t *domain_sid, smb_sid_t *sid)
{
int i;
if (sid == NULL || domain_sid == NULL)
return (B_FALSE);
if (domain_sid->sid_revision != sid->sid_revision ||
sid->sid_subauthcnt < domain_sid->sid_subauthcnt)
return (B_FALSE);
for (i = domain_sid->sid_subauthcnt - 1; i >= 0; --i)
if (domain_sid->sid_subauth[i] != sid->sid_subauth[i])
return (B_FALSE);
if (bcmp(&domain_sid->sid_authority, &sid->sid_authority,
NT_SID_AUTH_MAX))
return (B_FALSE);
return (B_TRUE);
}
/*
* smb_sid_tostr
*
* Fill in the passed buffer with the string form of the given
* binary sid.
*/
void
smb_sid_tostr(const smb_sid_t *sid, char *strsid)
{
char *p = strsid;
int i;
if (sid == NULL || strsid == NULL)
return;
(void) sprintf(p, "S-%d-", sid->sid_revision);
while (*p)
p++;
for (i = 0; i < NT_SID_AUTH_MAX; ++i) {
if (sid->sid_authority[i] != 0 || i == NT_SID_AUTH_MAX - 1) {
(void) sprintf(p, "%d", sid->sid_authority[i]);
while (*p)
p++;
}
}
for (i = 0; i < sid->sid_subauthcnt && i < NT_SID_SUBAUTH_MAX; ++i) {
(void) sprintf(p, "-%u", sid->sid_subauth[i]);
while (*p)
p++;
}
}
/*
* smb_sid_fromstr
*
* Converts a SID in string form to a SID structure. There are lots of
* simplifying assumptions in here. The memory for the SID is allocated
* as if it was the largest possible SID; the caller is responsible for
* freeing the memory when it is no longer required. We assume that the
* string starts with "S-1-" and that the authority is held in the last
* byte, which should be okay for most situations. It also assumes the
* sub-authorities are in decimal format.
*
* On success, a pointer to a SID is returned. Otherwise a null pointer
* is returned.
*/
#if defined(_KERNEL) || defined(_FAKE_KERNEL)
smb_sid_t *
smb_sid_fromstr(const char *sidstr)
{
smb_sid_t *sid;
smb_sid_t *retsid;
const char *p;
int size;
uint8_t i;
unsigned long sua;
if (sidstr == NULL)
return (NULL);
if (strncmp(sidstr, "S-1-", 4) != 0)
return (NULL);
size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t));
sid = kmem_zalloc(size, KM_SLEEP);
sid->sid_revision = NT_SID_REVISION;
sua = 0;
(void) ddi_strtoul(&sidstr[4], 0, 10, &sua);
sid->sid_authority[5] = (uint8_t)sua;
for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) {
while (*p && *p == '-')
++p;
if (*p < '0' || *p > '9') {
kmem_free(sid, size);
return (NULL);
}
sua = 0;
(void) ddi_strtoul(p, 0, 10, &sua);
sid->sid_subauth[i] = (uint32_t)sua;
while (*p && *p != '-')
++p;
}
sid->sid_subauthcnt = i;
retsid = smb_sid_dup(sid);
kmem_free(sid, size);
return (retsid);
}
#else /* _KERNEL */
smb_sid_t *
smb_sid_fromstr(const char *sidstr)
{
smb_sid_t *sid;
const char *p;
int size;
uint8_t i;
if (sidstr == NULL)
return (NULL);
if (strncmp(sidstr, "S-1-", 4) != 0)
return (NULL);
size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t));
if ((sid = malloc(size)) == NULL)
return (NULL);
bzero(sid, size);
sid->sid_revision = NT_SID_REVISION;
sid->sid_authority[5] = atoi(&sidstr[4]);
for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) {
while (*p && *p == '-')
++p;
if (*p < '0' || *p > '9') {
free(sid);
return (NULL);
}
sid->sid_subauth[i] = strtoul(p, NULL, 10);
while (*p && *p != '-')
++p;
}
sid->sid_subauthcnt = i;
return (sid);
}
#endif /* _KERNEL */
/*
* smb_sid_type2str
*
* Returns the text name for a SID_NAME_USE value. The SID_NAME_USE
* provides the context for a SID, i.e. the type of resource to which
* it refers.
*/
char *
smb_sid_type2str(uint16_t snu_id)
{
static char *snu_name[] = {
"SidTypeSidPrefix",
"SidTypeUser",
"SidTypeGroup",
"SidTypeDomain",
"SidTypeAlias",
"SidTypeWellKnownGroup",
"SidTypeDeletedAccount",
"SidTypeInvalid",
"SidTypeUnknown",
"SidTypeComputer",
"SidTypeLabel"
};
if (snu_id < ((sizeof (snu_name)/sizeof (snu_name[0]))))
return (snu_name[snu_id]);
return (snu_name[SidTypeUnknown]);
}
static smb_sid_t *
smb_sid_alloc(size_t size)
{
smb_sid_t *sid;
#if defined(_KERNEL) || defined(_FAKE_KERNEL)
sid = kmem_alloc(size, KM_SLEEP);
#else
sid = malloc(size);
#endif
return (sid);
}
void
smb_sid_free(smb_sid_t *sid)
{
#if defined(_KERNEL) || defined(_FAKE_KERNEL)
if (sid == NULL)
return;
kmem_free(sid, smb_sid_len(sid));
#else
free(sid);
#endif
}