Files
2014-10-18 22:25:05 +02:00

1792 lines
47 KiB
C

/*
* libsam.c - SAM database functions, user and group editing
*
* Functions to edit SAM database, like adding and removing
* users to groups, list users and groups
* list user data and reset passwords
* low level SID handling functions
*
* 2013-aug: Cleaned up a bit for release, still some debug/strange things left
* 2013-aug: actually having functions doing listings in library is not good, bu
* have to do with that for now.
* 2013-apr-may: Functions for password reset, more group stuff etc
* 2012-oct: Split off from functions in chntpw.c
* 2012-jun-oct: Made routines for group handling (add/remove user from group etc)
*
* See HISTORY.txt for more detailed info on history.
*
*****
*
* Copyright (c) 1997-2013 Petter Nordahl-Hagen.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* See file GPL.txt for the full license.
*
*****
*
* Some information and ideas taken from pwdump by Jeremy Allison.
* More info from NTCrack by Jonathan Wilkins.
*
*/
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "ntreg.h"
#include "sam.h"
extern int gverbose; /* Ehm.. must get rid of this some day */
/* Strings for account bits fields */
char *acb_fields[16] = {
"Disabled" ,
"Homedir req." ,
"Passwd not req." ,
"Temp. duplicate" ,
"Normal account" ,
"NMS account" ,
"Domain trust act." ,
"Wks trust act." ,
"Srv trust act" ,
"Pwd don't expire" ,
"Auto lockout" ,
"(unknown 0x08)" ,
"(unknown 0x10)" ,
"(unknown 0x20)" ,
"(unknown 0x40)" ,
"(unknown 0x80)" ,
};
/* Number of paths we find group info under, needed by some later routines */
#define SAM_NUM_GROUPPATHS 2
/* Paths for group ID list*/
static char *SAM_GRPCPATHS[] = {
"\\SAM\\Domains\\Builtin\\Aliases",
"\\SAM\\Domains\\Account\\Aliases",
"" };
/* Paths for C (group data) value under group ID %08X */
static char *SAM_GRPCPATHID[] = {
"\\SAM\\Domains\\Builtin\\Aliases\\%08X\\C",
"\\SAM\\Domains\\Account\\Aliases\\%08X\\C",
"" };
/* Paths for users lists of group memberships, machine SID %s , user RID %08x
* each key contains one default value (no name) with TYPE indicating number of group IDs it contains
* value contents is then an array of 32 bit group IDs
*/
static char *SAM_GRPMEMBERSPATH[] = {
"\\SAM\\Domains\\Builtin\\Aliases\\Members\\%s\\%08X",
"\\SAM\\Domains\\Account\\Aliases\\Members\\%s\\%08X",
"" };
static char *SAM_GRPSIDPATH[] = {
"\\SAM\\Domains\\Builtin\\Aliases\\Members\\%s",
"\\SAM\\Domains\\Account\\Aliases\\Members\\%s",
"" };
/* Check if hive is SAM, and if it is, extract some
* global policy information from it, like lockout counts etc
* show = 1 means also print some more info
* Returns the number of allowed logins before lockout (locklimit)
* or -1 if error (not SAM, key not found etc)
*/
int sam_get_lockoutinfo(struct hive *hdesc, int show)
{
struct accountdb_F *f;
struct keyval *v;
if (hdesc->type == HTYPE_SAM) {
/* Get accoundb F value */
v = get_val2buf(hdesc, NULL, 0, ACCOUNTDB_F_PATH, REG_BINARY, TPF_VK);
if (!v) {
fprintf(stderr,"WARNING: Login counts data not found in SAM\n");
return (-1);
}
f = (struct accountdb_F *)&v->data;
if (show) {
printf("\n* SAM policy limits:\n");
printf("Failed logins before lockout is: %d\n",f->locklimit);
printf("Minimum password length : %d\n",f->minpwlen);
printf("Password history count : %d\n",f->minpwlen);
}
return(f->locklimit);
}
return(-1); /* Not SAM */
}
/* Try to decode and possibly change account lockout etc
* This is \SAM\Domains\Account\Users\<RID>\F
* It's size seems to always be 0x50.
* Params: RID - user ID, mode - 0 silent, 1 print info, 2 edit.
* Returns: ACB bits with high bit set if lockout count is >0
*/
short sam_handle_accountbits(struct hive *hdesc, int rid, int mode)
{
struct user_F *f;
char s[200];
struct keyval *v;
unsigned short acb;
int b;
int max_sam_lock;
if (hdesc->type != HTYPE_SAM) return(0);
/* Get users F value */
snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\F",rid);
v = get_val2buf(hdesc, NULL, 0, s, REG_BINARY, TPF_VK_EXACT);
if (!v) {
printf("Cannot find value <%s>\n",s);
return(0);
}
if (v->len < 0x48) {
printf("handle_F: F value is 0x%x bytes, need >= 0x48, unable to check account flags!\n",v->len);
FREE(v);
return(0);
}
max_sam_lock = sam_get_lockoutinfo(hdesc, 0);
f = (struct user_F *)&v->data;
acb = f->ACB_bits;
if (mode == 1) {
printf("Account bits: 0x%04x =\n",acb);
for (b=0; b < 15; b++) {
printf("[%s] %-15.15s | ",
(acb & (1<<b)) ? "X" : " ", acb_fields[b] );
if (b%3 == 2) printf("\n");
}
printf("\nFailed login count: %u, while max tries is: %u\n",f->failedcnt,max_sam_lock);
printf("Total login count: %u\n",f->logins);
}
if (mode == 2) { /* MODE = 2, reset to default sane sets of bits and null failed login counter */
acb |= ACB_PWNOEXP;
acb &= ~ACB_DISABLED;
acb &= ~ACB_AUTOLOCK;
f->ACB_bits = acb;
f->failedcnt = 0;
put_buf2val(hdesc, v, 0, s, REG_BINARY,TPF_VK_EXACT); /* TODO: Check error return */
printf("Unlocked!\n");
}
return (acb | ( (f->failedcnt > 0 && f->failedcnt >= max_sam_lock)<<15 ) | (acb & ACB_AUTOLOCK)<<15 | (acb & ACB_DISABLED)<<15);
}
/***** SID data handling routines **********/
/* Get machines SID as binary (raw data)
* str = pointer to buffer, first 20 bytes will be filled in
* returns true if found, else 0
*/
int sam_get_machine_sid(struct hive *hdesc, char *sidbuf)
{
struct accountdb_V *v;
struct keyval *kv;
uint32_t ofs;
uint32_t len;
/* Get accoundb V value */
kv = get_val2buf(hdesc, NULL, 0, ACCOUNTDB_V_PATH, REG_BINARY, TPF_VK);
if (!kv) {
fprintf(stderr,"sam_get_machine_sid: Machine SID not found in SAM\n");
return(0);
}
// hexdump(&(kv->data), 0, kv->len,1);
v = (struct accountdb_V *)&kv->data;
ofs = v->sid_ofs;
len = v->sid_len + 4;
ofs += 0x40;
if (len != SID_BIN_LEN) {
fprintf(stderr,"sam_get_machine_sid: WARNING: SID found, but it has len=%d instead of expected %d bytes\n",len,SID_BIN_LEN);
}
// printf("get_machine_sid: adjusted ofs = %x, len = %x (%d)\n",ofs,len,len);
memcpy(sidbuf, (char *)v+ofs, len);
// hexdump(sidbuf, 0, len, 1);
return(1);
}
/* Make string out of SID, in S-1-5 authority (NT authority)
* like S-1-5-21-516312364-151943033-2698651
* Will allocate return string (which can be of variable lenght)
* NOTE: caller must free it
* sidbuf = the SID binary data structure with it's type+counter first
*
* returns str:
* 6 chars athority prefix (S-1-5-)
* 4 * 10 digits (the 4 32 bit groups)
* 3 for the - between the groups
* 1 for null termination
* 50 chars
*/
char *sam_sid_to_string(struct sid_binary *sidbuf)
{
int cnt, i;
char *str = NULL;
// hexdump(sidbuf, 0, 24, 1);
if (sidbuf->revision != 1) {
fprintf(stderr,"sam_sid_to_string: DEBUG: first byte unexpected: %d\n",sidbuf->revision);
}
cnt = sidbuf->sections;
// printf("sid_to_string: DEBUG: sections = %d\n",cnt);
str = str_dup("S-");
str = str_catf(str, "%u-%u", sidbuf->revision, sidbuf->authority);
for (i = 0; i < cnt; i++) {
str = str_catf(str,"-%u",sidbuf->array[i]);
}
// printf("sid_to_string: returning <%s>\n",str);
return(str);
}
/* Stuff SID binary list into more easily handled arrays
* sidbuf = binary list buffer (not changed, may point into value structure)
* size = number of bytes of raw data
* returns pointer to array, terminated with NULL pointer.
* Keeps full binary data from each SID
* All array space is allocated, call sam_free_sid_array() to free it.
*/
struct sid_array *sam_make_sid_array(struct sid_binary *sidbuf, int size)
{
int num = 0;
int sidlen;
struct sid_binary *sb;
struct sid_array *array;
CREATE(array, struct sid_array, 1);
array[0].len = 0;
array[0].sidptr = NULL;
while (size > 0) {
sidlen = sidbuf->sections * 4 + 8;
// printf("make_sid_array: sidlen = %d\n",sidlen);
ALLOC(sb, 1, sidlen);
memcpy(sb, sidbuf, sidlen);
array[num].len = sidlen;
array[num].sidptr = sb;
sidbuf = (void *)sidbuf + sidlen;
size -= sidlen;
num++;
array = realloc(array, (num + 1) * sizeof(struct sid_array));
array[num].len = 0;
array[num].sidptr = NULL;
}
return(array);
}
/* Free the sid array (from the function above) */
void sam_free_sid_array(struct sid_array *array)
{
int num = 0;
while (array[num].sidptr) {
free(array[num].sidptr);
num++;
}
free(array);
}
/* Compare two SIDs, and return like strcmp */
int sam_sid_cmp(struct sid_binary *s1, struct sid_binary *s2)
{
int p;
if (!s1 && !s2) return(0);
if (!s1) return(-1);
if (!s2) return(1);
if (s1->sections < s2->sections) return(-1); /* s1 has shorter len, always smaller */
if (s1->sections > s2->sections) return(1); /* s1 has longer len, always larger */
/* Run compare since same length */
for (p = 0; p < s1->sections; p++) {
if (s1->array[p] < s2->array[p]) return (-1);
if (s1->array[p] > s2->array[p]) return (1);
}
/* At end. Thus equal */
return(0);
}
/************** GROUP DATA HANDLING ROUTINES ****************/
/* Get C value of a group ID, searching botg bult-in and user defined
* hdesc - hive
* grp - group ID
* returns pointer to value buffer or NULL if not found
*/
struct keyval *sam_get_grpC(struct hive *hdesc, int grp)
{
struct keyval *c = NULL;
int n = 0;
char g[200];
/* Try built-in groups first (administrators, user, guests etc) */
while (*SAM_GRPCPATHID[n] && !c) {
snprintf(g, 180, SAM_GRPCPATHID[n], grp);
c = get_val2buf(hdesc, NULL, 0, g, 0, TPF_VK_EXACT);
n++;
}
return(c);
}
/* Get list of group members for a group
* Will get the SID list (as binary) into a buffer that will be allocated
* according to the neccessary size (based on member count)
* NOTE: Caller must free the buffer when not needed any more
* grp = group ID
* sidarray = pointer to pointer to sid array which will be allocated
* Returns number of members in the group
*/
int sam_get_grp_members_sid(struct hive *hdesc, int grp, struct sid_array **sarray)
{
// char groupname[128];
struct sid_array *marray;
struct keyval *c = NULL;
struct group_C *cd;
// int grpnamoffs, grpnamlen;
int mofs, mlen;
c = sam_get_grpC(hdesc, grp);
if (c) {
cd = (struct group_C *)&c->data;
// grpnamoffs = cd->grpname_ofs + 0x34;
// grpnamlen = cd->grpname_len;
// cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen);
// printf("get_grp_members_sid: group %x named %s has %d members\n",grp,groupname,cd->grp_members);
mofs = cd->members_ofs;
mlen = cd->members_len;
// printf("get_grp_members_sid: mofs = %x, mlen = %x (%d)\n", mofs,mlen,mlen);
// printf("get_grp_members_sid: ajusted: mofs = %x, mlen = %x (%d)\n", mofs + 0x34 ,mlen,mlen);
// hexdump(&c->data, 0, c->len, 1);
// hexdump(&cd->data[mofs], 0, mlen, 1);
marray = sam_make_sid_array((struct sid_binary *)&cd->data[mofs], mlen);
*sarray = marray;
// sam_free_sid_array(marray);
free(c);
} else {
printf("Group info for %x not found!\n",grp);
*sarray = NULL;
return(0);
}
return(cd->grp_members);
}
/* Put list of group members back into group C structure
* grp = group ID
* sidarray = pointer to sid array
* Returns true if success
*/
int sam_put_grp_members_sid(struct hive *hdesc, int grp, struct sid_array *sarray)
{
char g[200];
char groupname[128];
struct keyval *c = NULL;
struct group_C *cd;
int grpnamoffs, grpnamlen;
int mofs, mlen;
int sidlen = 0;
void *sidptr;
int i, n;
char *str;
/* Try built-in groups first (administrators, user, guests etc) */
n = 0;
while (*SAM_GRPCPATHID[n] && !c) {
snprintf(g, 180, SAM_GRPCPATHID[n], grp);
c = get_val2buf(hdesc, NULL, 0, g, 0, TPF_VK_EXACT);
n++;
}
if (c) {
cd = (struct group_C *)&c->data;
grpnamoffs = cd->grpname_ofs + 0x34;
grpnamlen = cd->grpname_len;
cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen);
if (gverbose) printf("put_grp_members_sid: group %x named %s has %d members\n",grp,groupname,cd->grp_members);
mofs = cd->members_ofs;
mlen = cd->members_len;
if (gverbose) printf("put_grp_members_sid: ajusted: mofs = %x, mlen = %x (%d)\n", mofs + 0x34 ,mlen,mlen);
if (gverbose) hexdump(&c->data, 0, c->len, 1);
/* Get total size of new SID data */
for (i = 0; sarray[i].sidptr; i++) sidlen += sarray[i].len;
if (gverbose) printf("put_grp_members_sid: new count : %d, new sidlen: %x\n",i,sidlen);
/* Resize buffer with C structure */
c = realloc(c, 4 + mofs + sidlen + 0x34); /* offset of SIDs + sids lenght + pointer list at start */
c->len = 0x34 + mofs + sidlen;
cd = (struct group_C *)&c->data;
mofs = cd->members_ofs;
sidptr = &cd->data[mofs];
for (i = 0; sarray[i].sidptr; i++) {
if (gverbose) printf(" copying : %d len %x, at %x\n",i,sarray[i].len, sidptr);
str = sam_sid_to_string(sarray[i].sidptr);
if (gverbose) printf(" Member # %d = <%s>\n", i, str);
FREE(str);
memcpy(sidptr, sarray[i].sidptr, sarray[i].len);
sidptr += sarray[i].len;
}
cd->members_len = sidlen; /* Update member count in C struct */
cd->grp_members = i;
if (gverbose) hexdump(&c->data, 0, c->len, 1);
if (!put_buf2val(hdesc, c, 0, g, 0, TPF_VK_EXACT)) {
fprintf(stderr,"put_grp_members_sid: could not write back group info in value %s\n",g);
free(c);
return(0);
}
free(c);
} else {
printf("Group info for %x not found!\n",grp);
return(0);
}
return(1);
}
/* Get group IDs a user is member of
* rid = user ID
* returns: since value data is just an array of grp ids (4 bytes each),
* just return the keyval structure (size + data)
* caller must free() keyval
*/
struct keyval *sam_get_user_grpids(struct hive *hdesc, int rid)
{
char s[200];
struct sid_binary sid;
char *sidstr;
int nk = 0;
struct keyval *m = NULL;
struct keyval *result = NULL;
struct keyval *newresult = NULL;
int count = 0;
int size;
int n;
if (!rid || (hdesc->type != HTYPE_SAM)) return(NULL);
if (!sam_get_machine_sid(hdesc, (char *)&sid)) {
fprintf(stderr,"sam_get_user_grpids: Could not find machine SID\n");
return(0);
}
sidstr = sam_sid_to_string(&sid);
/* Get member list for user on this machine */
n = 0; /* Look up user RID under computer SID under builtin and account path */
while (*SAM_GRPMEMBERSPATH[n]) {
snprintf(s, 180, SAM_GRPMEMBERSPATH[n], sidstr, rid);
if (gverbose) printf("sam_get_user_grpids: member path: %s\n",s);
nk = trav_path(hdesc, 0, s, 0);
if (nk) { /* Found a key */
/* Now, the TYPE field is the number of groups the user is member of */
/* Don't we just love the inconsistent use of fields!! */
nk += 4;
count = get_val_type(hdesc,nk,"@",TPF_VK_EXACT);
if (count == -1) {
printf("sam_get_user_grpids: Cannot find default value <%s\\@>\n",s);
n++;
continue;
}
// printf("sam_get_user_grpids: User is member of %d groups:\n",count);
/* This is the data size */
size = get_val_len(hdesc,nk,"@",TPF_VK_EXACT);
/* It should be 4 bytes for each group */
// if (gverbose) printf("Data size %d bytes.\n",size);
if (size != count * 4) {
printf("sam_get_user_grpids: DEBUG: Size is not 4 * count! May not matter anyway. Continuing..\n");
}
m = get_val2buf(hdesc, NULL, nk, "@", 0, TPF_VK_EXACT);
if (!m) {
printf("sam_get_user_grpids: Could not get value data! Giving up.\n");
FREE(sidstr);
return(NULL);
}
/* At this point we have a value containing member list from this part of the tree */
/* Just append this one to the earlier ones */
newresult = reg_valcat(result, m);
FREE(m);
FREE(result);
result = newresult;
}
n++;
}
FREE(sidstr);
if (!result) {
/* This probably means user is not in any group. Seems to be the case
for a couple of XPs built in support / guest users. So just return */
if (gverbose) printf("sam_get_user_grpids: Cannot find RID under computer SID <%s>\n",s);
return(NULL);
}
// printf(" sam_get_user_grpids done\n");
return(result);
}
/* Put/set group IDs a user is member of
* rid = user ID
* val = keyval structure of data, actual value data is a list
* of ints, one per group
* returns true if successful setting the value
*/
int sam_put_user_grpids(struct hive *hdesc, int rid, struct keyval *val)
{
char s[200];
char news[200];
char ks[12];
struct nk_key *newkey = NULL;
struct sid_binary sid;
char *sidstr;
int n, grp, pnum;
int newcount = 0;
int nk = 0;
int count = 0;
struct keyvala *v;
struct keyvala *new;
struct keyvala entry;
/* Pointers to value lists for each group path in use */
struct keyval *p[SAM_NUM_GROUPPATHS];
if (!rid || (hdesc->type != HTYPE_SAM)) return(0);
if (!val) return(0);
#if 0
if (!val->len) {
printf("sam_put_user_grpids: zero list len\n");
// return(0);
}
#endif
v = (struct keyvala *)val;
if (!sam_get_machine_sid(hdesc, (char *)&sid)) {
fprintf(stderr,"sam_put_user_grpids: Could not find machine SID\n");
return(0);
}
sidstr = sam_sid_to_string(&sid);
for (n = 0; n < SAM_NUM_GROUPPATHS; n++) {
ALLOC(p[n], sizeof(struct keyvala), 1);
p[n]->len = 0;
p[n]->data = 0;
}
/* Split value list into relevant stuff for each path */
for (n = 0; n < val->len >> 2; n++) {
grp = v->data[n];
for (pnum = 0; pnum < SAM_NUM_GROUPPATHS; pnum++) {
snprintf(s, 180, SAM_GRPCPATHID[pnum], grp);
// printf("sam_put_user_grpids: split path: %s\n",s);
nk = trav_path(hdesc, 0, s, TPF_VK_EXACT); /* Check if group is in path?? */
if (nk) { /* Yup, it is here */
entry.data[0] = grp;
entry.len = 4;
// printf("sam_put_user_grpids: path match for grp ID: %x\n", entry.data[1]);
new = (struct keyvala *)reg_valcat( p[pnum], (struct keyval *)&entry);
FREE( p[pnum] );
p[pnum] = (struct keyval *)new;
}
}
}
/* Now put the lists into the correct place */
for (n = 0; n < SAM_NUM_GROUPPATHS; n++) {
/* Get member list for user on this machine */
snprintf(s,180,SAM_GRPMEMBERSPATH[n] ,sidstr, rid);
// printf("sam_put_user_grpids: putting for path: %s\n",s);
newcount = p[n]->len >> 2;
// printf("--- list for that path has len: %d\n",p[n]->len);
for (pnum = 0; pnum < p[n]->len >> 2; pnum++) {
new = (struct keyvala *)p[n];
// printf("%d : %x\n", pnum, new->data[pnum]);
}
/* Find users member list under this path */
nk = trav_path(hdesc, 0, s, 0);
if (!nk) {
/* User is not in any group in this path, see if we need to create key */
if (gverbose) printf("sam_put_user_grpids: Cannot find path <%s>\n",s);
if (!newcount) continue; /* Nothing to put there anyway, so just try next path */
snprintf(news,180,SAM_GRPSIDPATH[n] ,sidstr);
// snprintf(ks, 180, "%08X", rid);
// printf("sam_put_user_grpids: creating key <%s> on path <%s>\n",ks,news);
nk = trav_path(hdesc, 0, news, 0);
newkey = add_key(hdesc, nk+4, ks);
if (!newkey) {
fprintf(stderr,"sam_put_user_grpids: ERROR: creating group list key for RID <%08x> under path <%s>\n",rid,news);
abort();
}
nk = trav_path(hdesc, 0, s, 0);
if (!add_value(hdesc, nk+4, "@", 0)) {
fprintf(stderr,"sam_put_user_grpids: ERROR: creating group list default value for RID <%08x> under path <%s>\n",rid,news);
abort();
}
}
nk += 4;
/* Now, the TYPE field is the number of groups the user is member of */
count = get_val_type(hdesc, nk,"@", TPF_VK_EXACT);
if (count == -1) {
printf("sam_put_user_grpids: Cannot find value <%s\\@>\n",s);
return(1);
}
if (gverbose) printf("sam_put_user_grpids: User was member of %d groups:\n",count);
/* This is the data size */
/* It should be 4 bytes for each group */
if (gverbose) printf("Data size %d bytes.\n",p[n]->len);
if (p[n]->len != newcount << 2) {
printf("set_user_grpids: DEBUG: Size is not 4 * count! May not matter anyway. Continuing..\n");
}
if (gverbose) printf("sam_put_user_grpids: User is NOW member of %d groups:\n",newcount);
if (newcount == 0) { /* Seems windows removes the key and default subvalue when user not in any group */
// printf("sam_put_user_grpids: removing user reference for path %s\n",s);
del_value(hdesc, nk, "@", TPF_VK_EXACT);
nk = trav_path(hdesc, nk, "..", 0);
snprintf(s,180, "%08X", rid);
del_key(hdesc, nk + 4, s);
} else { /* Stuff back list into default value */
set_val_type(hdesc, nk, "@", TPF_VK_EXACT, newcount);
if (!put_buf2val(hdesc, p[n], nk, "@", 0, TPF_VK_EXACT) ) {
printf("sam_put_user_grpids: Could not set reg value data!\n");
return(0);
}
}
} /* for path loop */
FREE(sidstr);
for (n = 0; n < SAM_NUM_GROUPPATHS; n++) {
FREE(p[n]);
}
printf("sam_put_user_grpids: success exit\n");
return(1);
}
/********* GROUP / USER MANIPULATION ROUTINES **************/
/* Add user to a group
* rid = user RID
* grp = group ID
* return true if success
*/
int sam_add_user_to_grp(struct hive *hdesc, int rid, int grp)
{
struct keyvala *usrgrplist, *newusrgrplist;
struct sid_array *sarray, *narray;
struct sid_binary *usid;
struct sid_binary msid;
int members, newmembers;
char *str;
int ugcnt;
int o,n,hit,c;
unsigned int *og, *ng;
char s[200];
if (!rid || !grp || (hdesc->type !=HTYPE_SAM) ) return(0);
snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\V",rid);
if (!trav_path(hdesc, 0, s, TPF_VK_EXACT)) {
fprintf(stderr,"sam_add_user_to_grp: user # %x not found!\n",rid);
return(0);
}
/* Build user SID (add RID to machine SID) */
if (!sam_get_machine_sid(hdesc, (char *)&msid)) {
fprintf(stderr,"sam_add_user_to_grp: Could not find machine SID\n");
return(0);
}
/* well, and hope that machine SID is always same size here too */
ALLOC(usid, sizeof(struct sid_binary) +4, 1);
memcpy(usid, &msid, sizeof(struct sid_binary));
usid->array[4] = rid; /* Tack RID on at end */
usid->sections = 5;
str = sam_sid_to_string(usid);
if (gverbose) printf("add_user_to_grp: user SID is <%s>\n", str);
free(str);
/* With all of the above functions, it should now just be to get
* the list of groups the user account has listed under it
* and the list of users the group has listed under it
*/
usrgrplist = (struct keyvala *)sam_get_user_grpids(hdesc, rid);
if (!usrgrplist) {
printf("sam_add_user_to_grp: user # %x WAS IN NO GROUPS!\n",rid);
/* So make new blank list for it */
ALLOC(usrgrplist, sizeof(struct keyvala), 1);
usrgrplist->len = 0;
usrgrplist->data[0] = 0;
}
members = sam_get_grp_members_sid(hdesc, grp, &sarray);
if (!sarray) {
printf("sam_add_user_to_grp: group # %x not found!\n",grp);
FREE(usrgrplist);
return(0);
}
/* Add the group to the users list of groups it is member of */
ugcnt = usrgrplist->len >> 2; /* Count of groups already on user */
/* Allocate new larger usrgrplist for one more entry */
ALLOC(newusrgrplist, usrgrplist->len + 4 + 4, 1);
bzero(newusrgrplist, usrgrplist->len + 4 + 4); /* for sanity.. */
newusrgrplist->len = usrgrplist->len + 4;
og = (unsigned int *)&usrgrplist->data;
ng = (unsigned int *)&newusrgrplist->data;
if (gverbose) printf("usrgrplist-len = %d\n", usrgrplist->len);
#if 0 /* If list should be sorted, but seems windows does not do that? */
/* Copy over users group list, adding in where needed */
hit = 0;
for (o = 0, n = 0; o < ugcnt; o++, n++) {
printf(":: %d %d : %x\n",o,n,og[o]);
if (og[o] == grp) { /* Was already in there, so just don't increase size.. */
newusrgrplist->len = usrgrplist->len;
hit = 1;
}
if (og[o] > grp && !hit) {
ng[n++] = grp; /* Next is higher, so insert out rid */
hit = 1;
printf(" -- insert\n");
}
ng[n] = og[o];
}
printf("n = %d\n",n);
if (!hit) ng[n] = grp; /* Insert at end if we run down */
#endif
/* Copy over users group list, checking if already there */
hit = 0;
for (o = 0; o < ugcnt; o++) {
if (gverbose) printf(":: %d : %x\n",o,og[o]);
if (og[o] == grp) { /* Was already in there, so just don't increase size.. */
newusrgrplist->len = usrgrplist->len;
hit = 1;
if (gverbose) printf(" -- match\n");
}
ng[o] = og[o];
}
if (gverbose) printf(" - end of list at o = %d\n",o);
if (!hit) ng[o] = grp; /* Just stuff new group in at end if not already in list */
if (gverbose) {
for (o = 0; o < (newusrgrplist->len >> 2); o++) {
printf("grp index %d = %08x\n", o, ng[o]);
}
}
/* And then we add the user SID into the groups list of members */
if (gverbose) {
printf("add_user_to_grp: grp memberlist BEFORE:\n");
for (o = 0; sarray[o].sidptr; o++) {
str = sam_sid_to_string(sarray[o].sidptr);
printf(" Member # %d = <%s>\n", o, str);
FREE(str);
}
}
newmembers = members + 1;
ALLOC(narray, sizeof(struct sid_array) * (newmembers + 2), 1); /* Add one entry size */
if (gverbose) printf("members = %d\n", members);
hit = 0;
for (o = 0, n = 0; o <= members; o++, n++) {
c = sam_sid_cmp(sarray[o].sidptr, usid); /* Compare slot with new SID */
if (gverbose) printf("sam_sid_cmp returns %d\n",c);
if (c == 0) {
newmembers--; /* Already there, don't change anything */
hit = 1;
}
if (!hit && ((c > 0) || !sarray[o].sidptr)) { /* Next is higher, insert new SID */
if (gverbose) printf(" -- add\n");
narray[n].len = usid->sections * 4 + 8; /* Hmm */
narray[n].sidptr = usid;
n++;
hit = 1;
}
narray[n].len = sarray[o].len;
narray[n].sidptr = sarray[o].sidptr;
}
if (gverbose) {
printf("add_user_to_grp: grp memberlist AFTER:\n");
for (o = 0; narray[o].sidptr; o++) {
str = sam_sid_to_string(narray[o].sidptr);
printf(" Member # %d = <%s>\n", o, str);
FREE(str);
}
}
/* Write new lists back to registry */
if (!sam_put_user_grpids(hdesc, rid, (struct keyval *)newusrgrplist)) {
fprintf(stderr, "add_user_to_grp: failed storing users group list\n");
} else if (!sam_put_grp_members_sid(hdesc, grp, narray)) {
fprintf(stderr,"add_user_to_grp: failed storing groups user list\n");
sam_put_user_grpids(hdesc, rid, (struct keyval *)usrgrplist); /* Try to roll back */
}
FREE(usrgrplist);
FREE(newusrgrplist);
sam_free_sid_array(narray);
FREE(sarray); /* Pointers was copied to narray, and freed above, just free the array here */
return(1);
}
/* Remove user from a group
* rid = user RID
* grp = group ID
* return true if success
*/
int sam_remove_user_from_grp(struct hive *hdesc, int rid, int grp)
{
struct keyvala *usrgrplist, *newusrgrplist;
struct sid_array *sarray, *narray;
struct sid_binary *usid;
struct sid_binary msid;
int members, newmembers;
char *str;
int ugcnt;
int o,n,hit,c;
unsigned int *og, *ng;
if (!rid || !grp || (hdesc->type != HTYPE_SAM)) return(0);
/* Build user SID (add RID to machine SID) */
if (!sam_get_machine_sid(hdesc, (char *)&msid)) {
fprintf(stderr,"sam_remove_user_from_grp: Could not find machine SID\n");
return(0);
}
/* well, and hope that machine SID is always same size here too */
ALLOC(usid, sizeof(struct sid_binary) +4, 1);
memcpy(usid, &msid, sizeof(struct sid_binary));
usid->array[4] = rid; /* Tack RID on at end */
usid->sections = 5;
if (gverbose) {
str = sam_sid_to_string(usid);
printf("remove_user_from_grp: user SID is <%s>\n", str);
free(str);
}
/* With all of the above functions, it should now just be to get
* the list of groups the user account has listed under it
* and the list of users the group has listed under it
*/
usrgrplist = (struct keyvala *)sam_get_user_grpids(hdesc, rid);
if (!usrgrplist) {
printf("remove_user_from_grp: user # %x not found!\n",rid);
return(0);
}
members = sam_get_grp_members_sid(hdesc, grp, &sarray);
if (!sarray) {
printf("remove_user_from_grp: group # %x not found!\n",grp);
FREE(usrgrplist);
return(0);
}
/* Add the group to the users list of groups it is member of */
ugcnt = usrgrplist->len >> 2; /* Count of groups already on user */
/* Allocate same size usrgrplist, since we don't know if we are in there and need to be removed */
ALLOC(newusrgrplist, usrgrplist->len + 4, 1);
bzero(newusrgrplist, usrgrplist->len + 4); /* for sanity.. */
newusrgrplist->len = usrgrplist->len;
og = (unsigned int *)&usrgrplist->data;
ng = (unsigned int *)&newusrgrplist->data;
if (gverbose) printf("usrgrplist-len = %d\n", usrgrplist->len);
/* Copy over users group list, if relevant group found, don't copy it over */
hit = 0;
for (o = 0; o < ugcnt; o++) {
if (gverbose) printf(":: %d : %x\n",o,og[o]);
if (og[o] == grp) { /* Group found */
hit = 1;
if (gverbose) printf(" -- match\n");
} else {
ng[o-hit] = og[o];
}
}
if (gverbose) printf(" - end of list at o = %d\n",o);
if (hit) {
newusrgrplist->len -= 4; /* Decrease size if found */
} else {
fprintf(stderr, "remove_user_from_grp: NOTE: group not in users list of groups, may mean user not member at all. Safe. Continuing.\n");
}
if (gverbose) {
for (o = 0; o < (newusrgrplist->len >> 2); o++) {
printf("grp index %d = %08x\n", o, ng[o]);
}
/* Remove the user SID from the groups list of members */
printf("remove_user_from_grp: grp memberlist BEFORE:\n");
for (o = 0; sarray[o].sidptr; o++) {
str = sam_sid_to_string(sarray[o].sidptr);
printf(" Member # %d = <%s>\n", o, str);
FREE(str);
}
}
newmembers = members;
ALLOC(narray, sizeof(struct sid_array) * (newmembers + 2), 1);
if (gverbose) printf("members = %d\n", members);
hit = 0;
for (o = 0, n = 0; o <= members; o++, n++) {
c = sam_sid_cmp(sarray[o].sidptr, usid); /* Compare slot with new SID */
if (gverbose) printf("sid_cmp returns %d\n",c);
if (c == 0) {
newmembers--; /* Found, skip copy and decrease list size */
hit = 1;
n--;
} else {
narray[n].len = sarray[o].len; /* Copy entry */
narray[n].sidptr = sarray[o].sidptr;
}
}
if (!hit) fprintf(stderr, "remove_user_from_grp: NOTE: user not in groups list of users, may mean user was not member at all. Does not matter, continuing.\n");
if (gverbose) {
printf("remove_user_from_grp: grp memberlist AFTER:\n");
for (o = 0; narray[o].sidptr; o++) {
str = sam_sid_to_string(narray[o].sidptr);
printf(" Member # %d = <%s>\n", o, str);
FREE(str);
}
}
/* Write new lists back to registry */
if (!sam_put_user_grpids(hdesc, rid, (struct keyval *)newusrgrplist)) {
fprintf(stderr, "remove_user_from_grp: failed storing users group list\n");
} else if (!sam_put_grp_members_sid(hdesc, grp, narray)) {
fprintf(stderr,"remvoe_user_from_grp: failed storing groups user list\n");
sam_put_user_grpids(hdesc, rid, (struct keyval *)usrgrplist); /* Try to roll back */
}
FREE(usrgrplist);
FREE(newusrgrplist);
sam_free_sid_array(narray);
FREE(sarray); /* Pointers was copied to narray, and freed above, just free the array here */
return(1);
}
/* TODO: So.. having listing functions in library.. should better be handled by tools.. */
/* List users membership or check if admin (is in admin group)
* rid - users rid
* check - if 1 just check if admin, do not list
* returns true if user is admin
*/
int sam_list_user_groups(struct hive *hdesc, int rid, int check)
{
char groupname[128];
struct keyval *m = NULL, *c = NULL;
struct group_C *cd;
unsigned int *grps;
int count = 0, isadmin = 0;
int i, grp, grpnamoffs, grpnamlen;
if (!rid || (hdesc->type != HTYPE_SAM) ) return(0);
m = sam_get_user_grpids(hdesc, rid);
if (!m) return(0);
grps = (unsigned int *)&m->data;
count = m->len >> 2;
for (i = 0; i < count; i++) {
grp = grps[i];
if (!check) printf("%08x ",grp);
if (grp == 0x220) isadmin = 1;
if (!check) {
c = sam_get_grpC(hdesc, grp);
if (c) {
cd = (struct group_C *)&c->data;
grpnamoffs = cd->grpname_ofs + 0x34;
grpnamlen = cd->grpname_len;
cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen);
printf("= %s (which has %d members)\n",groupname,cd->grp_members);
// get_grp_members_sid(grp, &sidbuf);
} else {
printf("Group info for %x not found!\n",grp);
}
}
}
free(m);
return(isadmin);
}
/* List users in SAM file
* readable - 1 = list in human readable form, 0 = colon-separated, 2 = quiet, no ouput (find admin)
* return logic:
* If no users / error: 0
* If only 0x1f4 (built-in adminsitrator) is admin (or no one at all in admin group), return 0x1f4
* Else return lowest numbered user that is in admin group
*
* Fields ouput in parsable listing (all numbers in hex)
* rid:username:isadmin:acb:hashlen
* rid = User RID
* isadmin = (boolean flag) 1 user is admin, 0 is not
* acb = ACB account bits
* hashlen = lenght of password hash, 14 is normal if has passwd, 4 if blank
*/
char SAMdaunPATH[] = "\\SAM\\Domains\\Account\\Users\\Names\\";
int sam_list_users(struct hive *hdesc, int readable)
{
char s[200];
struct keyval *v;
int nkofs /* ,vkofs */ ;
int rid;
int count = 0, countri = 0;
int ntpw_len;
int admrid = 0x1f4;
int isadm;
unsigned short acb;
struct user_V *vpwd;
struct ex_data ex;
if (hdesc->type != HTYPE_SAM) return(0);
nkofs = trav_path(hdesc, 0, SAMdaunPATH, 0);
if (!nkofs) {
printf("sam_list_users: Cannot find usernames in registry! (is this a SAM-hive?)\n");
return(0);
}
if (readable == 1) printf("| RID -|---------- Username ------------| Admin? |- Lock? --|\n");
while ((ex_next_n(hdesc, nkofs+4, &count, &countri, &ex) > 0)) {
/* Extract the value out of the username-key, value is RID */
snprintf(s,180,"%s%s\\@",SAMdaunPATH, ex.name);
rid = get_dword(hdesc, 0, s, TPF_VK_EXACT|TPF_VK_SHORT);
/* Now that we have the RID, build the path to, and get the V-value */
snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\V",rid);
v = get_val2buf(hdesc, NULL, 0, s, REG_BINARY, TPF_VK_EXACT);
if (!v) {
printf("Cannot find value <%s>\n",s);
return(1);
}
if (v->len < 0xcc) {
printf("sam_list_users: Value <%s> is too short (only %d bytes) to be a SAM user V-struct!\n",
s, v->len);
} else {
vpwd = (struct user_V *)&(v->data);
ntpw_len = vpwd->ntpw_len;
acb = sam_handle_accountbits(hdesc, rid, 0);
isadm = sam_list_user_groups(hdesc, rid, 1);
if (isadm && rid != 0x1f4) { /* Found an non-built-in administrator */
if (admrid == 0x1f4) admrid = rid; /* Prefer anything over built-in one */
if (rid < admrid) admrid = rid;
}
if (readable == 1) {
printf("| %04x | %-30.30s | %-6s | %-8s |\n",
rid, ex.name, ( isadm ? "ADMIN" : "") , ( acb & 0x8000 ? "dis/lock" : (ntpw_len < 16) ? "*BLANK*" : "") );
} else if (readable == 0) {
printf("%04x:%s:%d:%x:%x\n",
rid, ex.name, isadm , acb, ntpw_len );
}
// change_pw( (char *)&v->data , rid, v->len, (*automode == 'l') ? 2 : 1);
}
FREE(v);
FREE(ex.name);
}
return(admrid);
}
/* Get username when we have a RID
* hdesc - hive
* rid - just that.. :)
* returns allocated string with username, caller must free it
* or NULL if RID not found in local databse
*/
char *sam_get_username(struct hive *hdesc, int rid)
{
char s[200];
char *username = NULL;
int username_offset,username_len;
struct user_V *v;
struct keyval *value;
int vlen;
char *vp;
snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\V",rid);
value = get_val2buf(hdesc, NULL, 0, s, REG_BINARY, TPF_VK_EXACT);
if (!value) {
printf(" sam_get_username: ERROR: User with RID 0x%x not found, path <%s>\n",rid,s);
return(NULL);
}
vlen = value->len;
if (vlen < 0xcc) {
printf(" sam_get_username: Value <%s> is too short (only %d bytes) to be a SAM user V-struct!\n",
s, vlen);
FREE(value);
return(NULL);
}
v = (struct user_V *)&value->data;
vp = (char *)&value->data;
username_offset = v->username_ofs;
username_len = v->username_len;
if(username_len <= 0 || username_len > vlen ||
username_offset <= 0 || username_offset >= vlen)
{
printf(" sam_get_username: Not a legal V struct? (negative struct lengths)\n");
FREE(value);
return(0);
}
/* Offsets in top of struct is relative to end of pointers, adjust */
username_offset += 0xCC;
ALLOC(username, 2, (username_len >> 1) + 4);
*username = 0;
cheap_uni2ascii(vp + username_offset,username,username_len);
if (gverbose) {
printf("RID : %04d [%04x]\n",rid,rid);
printf("Username: %s\n",username);
}
FREE(value);
return(username);
}
/* Get username from SID:
* Local database if matching machine SID
* Well known SIDs if in list
* Else probably domain SID, don't know how to find more info
*
* sid = sid to extract RID from
*
* returns a string (which must be free()d) or NULL if not able to resolve
*/
char *sam_get_username_from_sid(struct hive *hdesc, struct sid_binary *sid)
{
int rid;
char *str;
int i;
struct sid_binary msid;
struct known_sidentry {
int val;
char *name;
};
const struct known_sidentry ntauthority_table[] = {
{ 4, "INTERACTIVE" },
{ 11, "Authenticated Users" },
{ 17, "IUSR" },
{ 20, "NETWORK SERVICE" },
{ 0, "" }
};
if (!sid) return(NULL);
if(sid->sections < 1) return(NULL);
rid = sid->array[sid->sections-1];
if (sid->sections != 5) {
// fprintf(stderr," WARNING: sam_get_rid_from_sid: Strange size SID, sections = %d, not 5, got rid = %d\n",sid->sections, rid);
if (sid->sections == 1) {
if (sid->authority == 5) { /* We only handle S-1-5 (NTAUTHORITY) known names yet */
str = str_dup("NT AUTHORITY\\");
for (i = 0; ntauthority_table[i].val; i++) {
if (rid == ntauthority_table[i].val) {
str = str_cat(str, ntauthority_table[i].name);
return(str);
}
}
} else { /* Not NT AUTHORITY */
return(sam_sid_to_string(sid));
}
} /* sections */
return(sam_sid_to_string(sid));
}
if (sam_get_machine_sid(hdesc, (char *)&msid)) {
sid->sections--; /* Don't compare RID part */
if (!sam_sid_cmp(sid, &msid)) {
sid->sections++;
return(sam_get_username(hdesc, rid)); /* Match, find and return local username */
} else {
sid->sections++;
return(sam_sid_to_string(sid)); /* No match with local machine SID, so just return SID string */
}
}
/* If we get here we don't have a machine SID, so, well, try to get a local name anyway */
return(sam_get_username(hdesc, rid));
}
/* List groups in SAM
* hdesc - the hive
* listmembers - true = list groups members also, else just group id/name etc
* human = human readable form (true) or parsable (false)
*
* Format of group list:
* grpid:grpname:membercount
* Format of membership list:
* grpid:grpname:index:rid:username:usersid
* where index is just the ordinal number in the groups membership list (starts at 0)
* grpid and rid is in hex
*/
void sam_list_groups(struct hive *hdesc, int listmembers, int human) {
struct ex_data ex;
struct sid_array *sids = NULL;
int nkofs;
unsigned int grp;
int count,countri;
struct keyval *c = NULL;
struct group_C *cd;
int grpnamoffs, grpnamlen, i;
char groupname[200];
char *str;
char *username;
int pnum = 0;
if (hdesc->type != HTYPE_SAM) return;
while (*SAM_GRPCPATHS[pnum]) {
// printf(" -- grp C list path: %s\n",SAM_GRPCPATHS[pnum]);
nkofs = trav_path(hdesc, 0, SAM_GRPCPATHS[pnum], 0);
if (!nkofs) {
printf(" list_groups: Cannot find group list in registry! (is this a SAM-hive?)\n");
return;
}
/* Pick up all subkeys here, they are local groups */
count = 0;
countri = 0;
while ((ex_next_n(hdesc, nkofs+4, &count, &countri, &ex) > 0)) {
// printf("Group ID %s\n",ex.name);
sscanf(ex.name,"%x",&grp);
/* Groups keys have a C value, get it and pick up the name etc */
/* Some other keys also exists (Members, Names at least), but we skip them */
c = get_val2buf(hdesc, NULL, ex.nkoffs+4, "C", 0, TPF_VK_EXACT);
if (c) {
cd = (struct group_C *)&c->data;
grpnamoffs = cd->grpname_ofs + 0x34;
grpnamlen = cd->grpname_len;
cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen);
if (human) printf("=== Group #%4x : %s\n",grp,groupname);
else if (!listmembers) printf("%x:%s:%d\n",grp,groupname,cd->grp_members);
if (listmembers) {
sam_get_grp_members_sid(hdesc, grp, &sids);
for (i = 0; sids[i].sidptr; i++) {
str = sam_sid_to_string(sids[i].sidptr);
username = sam_get_username_from_sid(hdesc, sids[i].sidptr);
if (human) printf(" %3d | %04x | %-31s | <%s>\n", i, sids[i].sidptr->array[sids[i].sidptr->sections-1], username, str);
else printf("%x:%s:%d:%x:%s:%s\n", grp, groupname, i, sids[i].sidptr->array[sids[i].sidptr->sections-1], username, str);
FREE(username);
FREE(str);
}
sam_free_sid_array(sids);
}
} /* if c */
}
pnum++;
} /* path loop */
}
/* Get groupname when we have a group ID
* hdesc - hive
* grpid - just that.. :)
* returns allocated string with username, caller must free it
* or NULL if RID not found in local databse
*/
char *sam_get_groupname(struct hive *hdesc, int grpid)
{
struct keyval *value = NULL;
struct group_C *cd;
int grpnamoffs;
int grpnamlen;
char *groupname = NULL;
value = sam_get_grpC(hdesc, grpid);
if (!value) {
printf(" sam_get_groupname: ERROR: Group ID 0x%x not found\n",grpid);
return(NULL);
}
/* Offsets in top of struct is relative to end of pointers, adjust */
cd = (struct group_C *)&value->data;
grpnamoffs = cd->grpname_ofs + 0x34;
grpnamlen = cd->grpname_len;
ALLOC(groupname, 2, (grpnamlen >> 1) + 4);
*groupname = 0;
cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen);
// printf("==== Group #%4x : %s\n",grpid,groupname);
FREE(value);
return(groupname);
}
/* Reset users password
* hdesc - the HIVE :)
* rid - the users RID
*
* Returns: 0 = OK, 1 = error (for use in exit())
*/
int sam_reset_pw(struct hive *hdesc, int rid)
{
char *vp;
static char username[128],fullname[128];
int username_offset,username_len;
int fullname_offset,fullname_len;
int ntpw_len,lmpw_len,ntpw_offs,lmpw_offs;
int vlen;
struct user_V *v;
struct keyval *value;
char s[200];
if (!hdesc || !rid) return(1);
/* Now that we have the RID, build the path to, and get the V-value */
snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\V",rid);
value = get_val2buf(hdesc, NULL, 0, s, REG_BINARY, TPF_VK_EXACT);
if (!value) {
printf(" sam_reset_pw: ERROR: User with RID 0x%x not found, path <%s>\n",rid,s);
return(1);
}
vlen = value->len;
if (vlen < 0xcc) {
printf(" sam_reset_pw: Value <%s> is too short (only %d bytes) to be a SAM user V-struct!\n",
s, vlen);
return(1);
}
v = (struct user_V *)&value->data;
vp = (char *)&value->data;
username_offset = v->username_ofs;
username_len = v->username_len;
fullname_offset = v->fullname_ofs;
fullname_len = v->fullname_len;
lmpw_offs = v->lmpw_ofs;
lmpw_len = v->lmpw_len;
ntpw_offs = v->ntpw_ofs;
ntpw_len = v->ntpw_len;
if (gverbose) {
printf(" lmpw_offs: 0x%x, lmpw_len: %d (0x%x)\n",lmpw_offs,lmpw_len,lmpw_len);
printf(" ntpw_offs: 0x%x, ntpw_len: %d (0x%x)\n",ntpw_offs,ntpw_len,ntpw_len);
}
*username = 0;
*fullname = 0;
if(username_len <= 0 || username_len > vlen ||
username_offset <= 0 || username_offset >= vlen ||
fullname_len < 0 || fullname_len > vlen ||
lmpw_offs < 0 || lmpw_offs >= vlen)
{
printf(" sam_reset_pw: Not a legal V struct? (negative struct lengths)\n");
FREE(value);
return(0);
}
/* Offsets in top of struct is relative to end of pointers, adjust */
username_offset += 0xCC;
fullname_offset += 0xCC;
ntpw_offs += 0xCC;
lmpw_offs += 0xCC;
cheap_uni2ascii(vp + username_offset,username,username_len);
cheap_uni2ascii(vp + fullname_offset,fullname,fullname_len);
if (gverbose) {
printf("RID : %04d [%04x]\n",rid,rid);
printf("Username: %s\n",username);
printf("fullname: %s\n",fullname);
}
/* Setting hash lengths to zero seems to make NT think it is blank
* However, we probably leak about 40 bytes since I am to lazy to adjust the rest
* of the V structure.
*/
v->ntpw_len = 0;
v->lmpw_len = 0;
if (!(put_buf2val(hdesc, value, 0, s, REG_BINARY, TPF_VK_EXACT))) {
printf(" reset_pw: Failed to write updated <%s> to registry! Password change not completed!\n",s);
FREE(value);
return(1);
}
if (gverbose) printf(" reset_pw: Password cleared for user %s\n",username);
FREE(value);
return(0);
}
/* Reset password of ALL admin users
* hdesc - hive
* list - if true, list some info about users processed
*/
int sam_reset_all_pw(struct hive *hdesc, int list)
{
char s[200];
struct keyval *v;
int nkofs;
int rid;
int isadm;
int count = 0;
int countri = 0;
int fail = 0;
struct ex_data ex;
if (hdesc->type != HTYPE_SAM) return(0);
nkofs = trav_path(hdesc, 0, SAMdaunPATH, 0);
if (!nkofs) {
printf("sam_reset_all_pw: Cannot find usernames in registry! (is this a SAM-hive?)\n");
return(1);
}
while ((ex_next_n(hdesc, nkofs+4, &count, &countri, &ex) > 0)) {
/* Extract the value out of the username-key, value is RID */
snprintf(s,180,"%s%s\\@",SAMdaunPATH, ex.name);
rid = get_dword(hdesc, 0, s, TPF_VK_EXACT|TPF_VK_SHORT);
/* Now that we have the RID, build the path to, and get the V-value */
snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\V",rid);
v = get_val2buf(hdesc, NULL, 0, s, REG_BINARY, TPF_VK_EXACT);
if (!v) {
printf("sam_reset_all_pw: Cannot find value <%s>\n",s);
return(1);
}
if (v->len < 0xcc) {
printf("sam_reset_all_pw: Value <%s> is too short (only %d bytes) to be a SAM user V-struct!\n",
s, v->len);
} else {
isadm = sam_list_user_groups(hdesc, rid, 1);
if (isadm) {
if (list) printf("Reset user :%04x:%s\n", rid, ex.name );
fail |= sam_reset_pw(hdesc, rid);
}
}
FREE(v);
FREE(ex.name);
}
return(fail);
}