#include <strings.h>
#include <smbsrv/libsmb.h>
extern int smb_pwd_num(void);
extern int smb_lgrp_numbydomain(smb_domain_type_t, int *);
static uint32_t smb_sam_lookup_user(char *, smb_sid_t **);
static uint32_t smb_sam_lookup_group(char *, smb_sid_t **);
typedef struct smb_lwka {
uint32_t lwka_rid;
char *lwka_name;
uint16_t lwka_type;
} smb_lwka_t;
static smb_lwka_t lwka_tbl[] = {
{ 500, "Administrator", SidTypeUser },
{ 501, "Guest", SidTypeUser },
{ 502, "KRBTGT", SidTypeUser },
{ 512, "Domain Admins", SidTypeGroup },
{ 513, "Domain Users", SidTypeGroup },
{ 514, "Domain Guests", SidTypeGroup },
{ 516, "Domain Controllers", SidTypeGroup },
{ 517, "Cert Publishers", SidTypeGroup },
{ 518, "Schema Admins", SidTypeGroup },
{ 519, "Enterprise Admins", SidTypeGroup },
{ 520, "Global Policy Creator Owners", SidTypeGroup },
{ 533, "RAS and IAS Servers", SidTypeGroup }
};
#define SMB_LWKA_NUM (sizeof (lwka_tbl)/sizeof (lwka_tbl[0]))
static smb_lwka_t *smb_lwka_lookup_name(char *);
static smb_lwka_t *smb_lwka_lookup_sid(smb_sid_t *);
uint32_t
smb_sam_lookup_name(char *domain, char *name, uint16_t type,
smb_account_t *account)
{
smb_domain_t di;
smb_sid_t *sid;
uint32_t status;
smb_lwka_t *lwka;
bzero(account, sizeof (smb_account_t));
if (domain != NULL) {
if (!smb_domain_lookup_name(domain, &di) ||
(di.di_type != SMB_DOMAIN_LOCAL))
return (NT_STATUS_NOT_FOUND);
if (smb_strcasecmp(domain, di.di_nbname, 0) != 0)
return (NT_STATUS_NONE_MAPPED);
} else {
if (!smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di))
return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
}
if (smb_strcasecmp(name, di.di_nbname, 0) == 0) {
account->a_type = SidTypeDomain;
account->a_name = strdup("");
account->a_domain = strdup(di.di_nbname);
account->a_sid = smb_sid_dup(di.di_binsid);
account->a_domsid = smb_sid_dup(di.di_binsid);
account->a_rid = (uint32_t)-1;
if (!smb_account_validate(account)) {
smb_account_free(account);
return (NT_STATUS_NO_MEMORY);
}
return (NT_STATUS_SUCCESS);
}
if ((lwka = smb_lwka_lookup_name(name)) != NULL) {
sid = smb_sid_splice(di.di_binsid, lwka->lwka_rid);
type = lwka->lwka_type;
} else {
switch (type) {
case SidTypeUser:
status = smb_sam_lookup_user(name, &sid);
if (status != NT_STATUS_SUCCESS)
return (status);
break;
case SidTypeAlias:
status = smb_sam_lookup_group(name, &sid);
if (status != NT_STATUS_SUCCESS)
return (status);
break;
case SidTypeUnknown:
type = SidTypeUser;
status = smb_sam_lookup_user(name, &sid);
if (status == NT_STATUS_SUCCESS)
break;
if (status == NT_STATUS_NONE_MAPPED)
return (status);
type = SidTypeAlias;
status = smb_sam_lookup_group(name, &sid);
if (status != NT_STATUS_SUCCESS)
return (status);
break;
default:
return (NT_STATUS_INVALID_PARAMETER);
}
}
account->a_name = strdup(name);
account->a_sid = sid;
account->a_domain = strdup(di.di_nbname);
account->a_domsid = smb_sid_split(sid, &account->a_rid);
account->a_type = type;
if (!smb_account_validate(account)) {
smb_account_free(account);
return (NT_STATUS_NO_MEMORY);
}
return (NT_STATUS_SUCCESS);
}
uint32_t
smb_sam_lookup_sid(smb_sid_t *sid, smb_account_t *account)
{
char hostname[MAXHOSTNAMELEN];
smb_passwd_t smbpw;
smb_group_t grp;
smb_lwka_t *lwka;
smb_domain_t di;
uint32_t rid;
uid_t id;
int id_type;
int rc;
bzero(account, sizeof (smb_account_t));
if (!smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di))
return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
if (smb_sid_cmp(sid, di.di_binsid)) {
account->a_type = SidTypeDomain;
account->a_name = strdup("");
account->a_domain = strdup(di.di_nbname);
account->a_sid = smb_sid_dup(sid);
account->a_domsid = smb_sid_dup(sid);
account->a_rid = (uint32_t)-1;
if (!smb_account_validate(account)) {
smb_account_free(account);
return (NT_STATUS_NO_MEMORY);
}
return (NT_STATUS_SUCCESS);
}
if (!smb_sid_indomain(di.di_binsid, sid)) {
return (NT_STATUS_NOT_FOUND);
}
if ((lwka = smb_lwka_lookup_sid(sid)) != NULL) {
account->a_type = lwka->lwka_type;
account->a_name = strdup(lwka->lwka_name);
} else {
id_type = SMB_IDMAP_UNKNOWN;
if (smb_idmap_getid(sid, &id, &id_type) != IDMAP_SUCCESS)
return (NT_STATUS_NONE_MAPPED);
switch (id_type) {
case SMB_IDMAP_USER:
account->a_type = SidTypeUser;
if (smb_pwd_getpwuid(id, &smbpw) == NULL)
return (NT_STATUS_NO_SUCH_USER);
account->a_name = strdup(smbpw.pw_name);
account->a_flags = smbpw.pw_flags;
break;
case SMB_IDMAP_GROUP:
account->a_type = SidTypeAlias;
(void) smb_sid_getrid(sid, &rid);
rc = smb_lgrp_getbyrid(rid, SMB_DOMAIN_LOCAL, &grp);
if (rc != SMB_LGRP_SUCCESS)
return (NT_STATUS_NO_SUCH_ALIAS);
account->a_name = strdup(grp.sg_name);
smb_lgrp_free(&grp);
break;
default:
return (NT_STATUS_NONE_MAPPED);
}
}
if (smb_getnetbiosname(hostname, MAXHOSTNAMELEN) == 0)
account->a_domain = strdup(hostname);
account->a_sid = smb_sid_dup(sid);
account->a_domsid = smb_sid_split(sid, &account->a_rid);
if (!smb_account_validate(account)) {
smb_account_free(account);
return (NT_STATUS_NO_MEMORY);
}
return (NT_STATUS_SUCCESS);
}
int
smb_sam_usr_cnt(void)
{
return (smb_pwd_num());
}
uint32_t
smb_sam_usr_groups(smb_sid_t *user_sid, smb_ids_t *gids)
{
smb_ids_t new_gids;
smb_id_t *ids, *new_ids;
smb_giter_t gi;
smb_group_t lgrp;
int i, gcnt, total_cnt;
uint32_t ret;
boolean_t member;
gcnt = 0;
if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS)
return (NT_STATUS_INTERNAL_ERROR);
while (smb_lgrp_iterate(&gi, &lgrp) == SMB_LGRP_SUCCESS) {
member = B_FALSE;
if (smb_lgrp_is_member(&lgrp, user_sid))
member = B_TRUE;
else for (i = 0, ids = gids->i_ids;
i < gids->i_cnt; i++, ids++) {
if (smb_lgrp_is_member(&lgrp, ids->i_sid)) {
member = B_TRUE;
break;
}
}
if (member)
gcnt++;
smb_lgrp_free(&lgrp);
}
smb_lgrp_iterclose(&gi);
if (gcnt == 0)
return (NT_STATUS_SUCCESS);
if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS)
return (NT_STATUS_INTERNAL_ERROR);
ret = 0;
new_gids.i_cnt = gids->i_cnt;
total_cnt = gids->i_cnt + gcnt;
new_gids.i_ids = malloc(total_cnt * sizeof (smb_id_t));
if (new_gids.i_ids == NULL) {
ret = NT_STATUS_NO_MEMORY;
goto out;
}
(void) memcpy(new_gids.i_ids, gids->i_ids,
gids->i_cnt * sizeof (smb_id_t));
new_ids = new_gids.i_ids + gids->i_cnt;
(void) memset(new_ids, 0, gcnt * sizeof (smb_id_t));
while (smb_lgrp_iterate(&gi, &lgrp) == SMB_LGRP_SUCCESS) {
member = B_FALSE;
if (smb_lgrp_is_member(&lgrp, user_sid))
member = B_TRUE;
else for (i = 0, ids = gids->i_ids;
i < gids->i_cnt; i++, ids++) {
if (smb_lgrp_is_member(&lgrp, ids->i_sid)) {
member = B_TRUE;
break;
}
}
if (member && (new_gids.i_cnt < (gids->i_cnt + gcnt))) {
new_ids->i_sid = smb_sid_dup(lgrp.sg_id.gs_sid);
if (new_ids->i_sid == NULL) {
smb_lgrp_free(&lgrp);
ret = NT_STATUS_NO_MEMORY;
goto out;
}
new_ids->i_attrs = lgrp.sg_attr;
new_ids++;
new_gids.i_cnt++;
}
smb_lgrp_free(&lgrp);
}
out:
smb_lgrp_iterclose(&gi);
if (ret != 0) {
if (new_gids.i_ids != NULL) {
ids = new_gids.i_ids + gids->i_cnt;
for (i = 0; i < gcnt; i++, ids++) {
smb_sid_free(ids->i_sid);
}
free(new_gids.i_ids);
}
return (ret);
}
free(gids->i_ids);
*gids = new_gids;
return (NT_STATUS_SUCCESS);
}
int
smb_sam_grp_cnt(smb_domain_type_t dtype)
{
int grpcnt;
int rc;
switch (dtype) {
case SMB_DOMAIN_BUILTIN:
rc = smb_lgrp_numbydomain(SMB_DOMAIN_BUILTIN, &grpcnt);
break;
case SMB_DOMAIN_LOCAL:
rc = smb_lgrp_numbydomain(SMB_DOMAIN_LOCAL, &grpcnt);
break;
default:
rc = SMB_LGRP_INVALID_ARG;
}
return ((rc == SMB_LGRP_SUCCESS) ? grpcnt : 0);
}
boolean_t
smb_sam_grp_ismember(const char *gname, smb_sid_t *sid)
{
smb_group_t grp;
boolean_t ismember = B_FALSE;
if (smb_lgrp_getbyname((char *)gname, &grp) == SMB_LGRP_SUCCESS) {
ismember = smb_lgrp_is_member(&grp, sid);
smb_lgrp_free(&grp);
}
return (ismember);
}
void
smb_account_free(smb_account_t *account)
{
free(account->a_name);
free(account->a_domain);
smb_sid_free(account->a_sid);
smb_sid_free(account->a_domsid);
bzero(account, sizeof (smb_account_t));
}
boolean_t
smb_account_validate(smb_account_t *account)
{
return ((account->a_name != NULL) && (account->a_sid != NULL) &&
(account->a_domain != NULL) && (account->a_domsid != NULL));
}
static uint32_t
smb_sam_lookup_user(char *name, smb_sid_t **sid)
{
smb_passwd_t smbpw;
if (smb_pwd_getpwnam(name, &smbpw) == NULL)
return (NT_STATUS_NO_SUCH_USER);
if (smb_idmap_getsid(smbpw.pw_uid, SMB_IDMAP_USER, sid)
!= IDMAP_SUCCESS)
return (NT_STATUS_NONE_MAPPED);
if (!smb_sid_islocal(*sid)) {
smb_sid_free(*sid);
return (NT_STATUS_NONE_MAPPED);
}
return (NT_STATUS_SUCCESS);
}
static uint32_t
smb_sam_lookup_group(char *name, smb_sid_t **sid)
{
smb_group_t grp;
if (smb_lgrp_getbyname(name, &grp) != SMB_LGRP_SUCCESS)
return (NT_STATUS_NO_SUCH_ALIAS);
*sid = smb_sid_dup(grp.sg_id.gs_sid);
smb_lgrp_free(&grp);
return ((*sid == NULL) ? NT_STATUS_NO_MEMORY : NT_STATUS_SUCCESS);
}
static smb_lwka_t *
smb_lwka_lookup_name(char *name)
{
int i;
for (i = 0; i < SMB_LWKA_NUM; i++) {
if (smb_strcasecmp(name, lwka_tbl[i].lwka_name, 0) == 0)
return (&lwka_tbl[i]);
}
return (NULL);
}
static smb_lwka_t *
smb_lwka_lookup_sid(smb_sid_t *sid)
{
uint32_t rid;
int i;
(void) smb_sid_getrid(sid, &rid);
if (rid > 999)
return (NULL);
for (i = 0; i < SMB_LWKA_NUM; i++) {
if (rid == lwka_tbl[i].lwka_rid)
return (&lwka_tbl[i]);
}
return (NULL);
}
boolean_t
smb_sid_islocal(smb_sid_t *sid)
{
smb_domain_t di;
boolean_t islocal = B_FALSE;
if (smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di))
islocal = smb_sid_indomain(di.di_binsid, sid);
return (islocal);
}
void
smb_ids_free(smb_ids_t *ids)
{
smb_id_t *id;
int i;
if ((ids != NULL) && (ids->i_ids != NULL)) {
id = ids->i_ids;
for (i = 0; i < ids->i_cnt; i++, id++)
smb_sid_free(id->i_sid);
free(ids->i_ids);
}
}