1 /*
2 * libsam.c - SAM database functions, user and group editing
3 *
4 * Functions to edit SAM database, like adding and removing
5 * users to groups, list users and groups
6 * list user data and reset passwords
7 * low level SID handling functions
8
9 *
10 * 2013-aug: Cleaned up a bit for release, still some debug/strange things left
11 * 2013-aug: actually having functions doing listings in library is not good, bu
12 * have to do with that for now.
13 * 2013-apr-may: Functions for password reset, more group stuff etc
14 * 2012-oct: Split off from functions in chntpw.c
15 * 2012-jun-oct: Made routines for group handling (add/remove user from group etc)
16 *
17 * See HISTORY.txt for more detailed info on history.
18 *
19 *****
20 *
21 * Copyright (c) 1997-2013 Petter Nordahl-Hagen.
22 *
23 * This program is free software; you can redistribute it and/or modify
24 * it under the terms of the GNU General Public License as published by
25 * the Free Software Foundation; version 2 of the License.
26 *
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 *
32 * See file GPL.txt for the full license.
33 *
34 *****
35 *
36 * Some information and ideas taken from pwdump by Jeremy Allison.
37 * More info from NTCrack by Jonathan Wilkins.
38 *
39 */
40
41
42 #include <stdio.h>
43 #include <sys/types.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47 #include "ntreg.h"
48 #include "sam.h"
49
50 extern int gverbose; /* Ehm.. must get rid of this some day */
51
52 /* Strings for account bits fields */
53
54 char *acb_fields[16] = {
55 "Disabled" ,
56 "Homedir req." ,
57 "Passwd not req." ,
58 "Temp. duplicate" ,
59 "Normal account" ,
60 "NMS account" ,
61 "Domain trust act." ,
62 "Wks trust act." ,
63 "Srv trust act" ,
64 "Pwd don't expire" ,
65 "Auto lockout" ,
66 "(unknown 0x08)" ,
67 "(unknown 0x10)" ,
68 "(unknown 0x20)" ,
69 "(unknown 0x40)" ,
70 "(unknown 0x80)" ,
71 };
72
73 /* Number of paths we find group info under, needed by some later routines */
74 #define SAM_NUM_GROUPPATHS 2
75
76 /* Paths for group ID list*/
77
78 static char *SAM_GRPCPATHS[] = {
79 "\\SAM\\Domains\\Builtin\\Aliases",
80 "\\SAM\\Domains\\Account\\Aliases",
81 "" };
82
83 /* Paths for C (group data) value under group ID %08X */
84 static char *SAM_GRPCPATHID[] = {
85 "\\SAM\\Domains\\Builtin\\Aliases\\%08X\\C",
86 "\\SAM\\Domains\\Account\\Aliases\\%08X\\C",
87 "" };
88
89 /* Paths for users lists of group memberships, machine SID %s , user RID %08x
90 * each key contains one default value (no name) with TYPE indicating number of group IDs it contains
91 * value contents is then an array of 32 bit group IDs
92 */
93
94 static char *SAM_GRPMEMBERSPATH[] = {
95 "\\SAM\\Domains\\Builtin\\Aliases\\Members\\%s\\%08X",
96 "\\SAM\\Domains\\Account\\Aliases\\Members\\%s\\%08X",
97 "" };
98
99 static char *SAM_GRPSIDPATH[] = {
100 "\\SAM\\Domains\\Builtin\\Aliases\\Members\\%s",
101 "\\SAM\\Domains\\Account\\Aliases\\Members\\%s",
102 "" };
103
104
105
106 /* Check if hive is SAM, and if it is, extract some
107 * global policy information from it, like lockout counts etc
108 * show = 1 means also print some more info
109 * Returns the number of allowed logins before lockout (locklimit)
110 * or -1 if error (not SAM, key not found etc)
111 */
112
113
sam_get_lockoutinfo(struct hive * hdesc,int show)114 int sam_get_lockoutinfo(struct hive *hdesc, int show)
115 {
116 struct accountdb_F *f;
117 struct keyval *v;
118
119 if (hdesc->type == HTYPE_SAM) {
120
121 /* Get accoundb F value */
122 v = get_val2buf(hdesc, NULL, 0, ACCOUNTDB_F_PATH, REG_BINARY, TPF_VK);
123 if (!v) {
124 fprintf(stderr,"WARNING: Login counts data not found in SAM\n");
125 return (-1);
126 }
127
128 f = (struct accountdb_F *)&v->data;
129
130 if (show) {
131 printf("\n* SAM policy limits:\n");
132 printf("Failed logins before lockout is: %d\n",f->locklimit);
133 printf("Minimum password length : %d\n",f->minpwlen);
134 printf("Password history count : %d\n",f->minpwlen);
135 }
136
137 return(f->locklimit);
138
139 }
140
141 return(-1); /* Not SAM */
142
143 }
144
145
146
147 /* Try to decode and possibly change account lockout etc
148 * This is \SAM\Domains\Account\Users\<RID>\F
149 * It's size seems to always be 0x50.
150 * Params: RID - user ID, mode - 0 silent, 1 print info, 2 edit.
151 * Returns: ACB bits with high bit set if lockout count is >0
152 */
153
sam_handle_accountbits(struct hive * hdesc,int rid,int mode)154 short sam_handle_accountbits(struct hive *hdesc, int rid, int mode)
155 {
156
157 struct user_F *f;
158 char s[200];
159 struct keyval *v;
160 unsigned short acb;
161 int b;
162 int max_sam_lock;
163
164 if (hdesc->type != HTYPE_SAM) return(0);
165
166 /* Get users F value */
167 snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\F",rid);
168 v = get_val2buf(hdesc, NULL, 0, s, REG_BINARY, TPF_VK_EXACT);
169 if (!v) {
170 printf("Cannot find value <%s>\n",s);
171 return(0);
172 }
173
174 if (v->len < 0x48) {
175 printf("handle_F: F value is 0x%x bytes, need >= 0x48, unable to check account flags!\n",v->len);
176 FREE(v);
177 return(0);
178 }
179
180 max_sam_lock = sam_get_lockoutinfo(hdesc, 0);
181
182 f = (struct user_F *)&v->data;
183 acb = f->ACB_bits;
184
185 if (mode == 1) {
186 printf("Account bits: 0x%04x =\n",acb);
187
188
189 for (b=0; b < 15; b++) {
190 printf("[%s] %-15.15s | ",
191 (acb & (1<<b)) ? "X" : " ", acb_fields[b] );
192 if (b%3 == 2) printf("\n");
193 }
194
195 printf("\nFailed login count: %u, while max tries is: %u\n",f->failedcnt,max_sam_lock);
196 printf("Total login count: %u\n",f->logins);
197 }
198
199 if (mode == 2) { /* MODE = 2, reset to default sane sets of bits and null failed login counter */
200 acb |= ACB_PWNOEXP;
201 acb &= ~ACB_DISABLED;
202 acb &= ~ACB_AUTOLOCK;
203 f->ACB_bits = acb;
204 f->failedcnt = 0;
205 put_buf2val(hdesc, v, 0, s, REG_BINARY,TPF_VK_EXACT); /* TODO: Check error return */
206 printf("Unlocked!\n");
207 }
208 return (acb | ( (f->failedcnt > 0 && f->failedcnt >= max_sam_lock)<<15 ) | (acb & ACB_AUTOLOCK)<<15 | (acb & ACB_DISABLED)<<15);
209 }
210
211
212 /***** SID data handling routines **********/
213
214
215 /* Get machines SID as binary (raw data)
216 * str = pointer to buffer, first 20 bytes will be filled in
217 * returns true if found, else 0
218 */
219
sam_get_machine_sid(struct hive * hdesc,char * sidbuf)220 int sam_get_machine_sid(struct hive *hdesc, char *sidbuf)
221 {
222
223 struct accountdb_V *v;
224 struct keyval *kv;
225 uint32_t ofs;
226 uint32_t len;
227
228 /* Get accoundb V value */
229 kv = get_val2buf(hdesc, NULL, 0, ACCOUNTDB_V_PATH, REG_BINARY, TPF_VK);
230 if (!kv) {
231 fprintf(stderr,"sam_get_machine_sid: Machine SID not found in SAM\n");
232 return(0);
233 }
234
235 // hexdump(&(kv->data), 0, kv->len,1);
236
237 v = (struct accountdb_V *)&kv->data;
238 ofs = v->sid_ofs;
239 len = v->sid_len + 4;
240 ofs += 0x40;
241
242 if (len != SID_BIN_LEN) {
243 fprintf(stderr,"sam_get_machine_sid: WARNING: SID found, but it has len=%d instead of expected %d bytes\n",len,SID_BIN_LEN);
244 }
245
246 // printf("get_machine_sid: adjusted ofs = %x, len = %x (%d)\n",ofs,len,len);
247
248
249 memcpy(sidbuf, (char *)v+ofs, len);
250
251 // hexdump(sidbuf, 0, len, 1);
252
253 return(1);
254 }
255
256 /* Make string out of SID, in S-1-5 authority (NT authority)
257 * like S-1-5-21-516312364-151943033-2698651
258 * Will allocate return string (which can be of variable lenght)
259 * NOTE: caller must free it
260 * sidbuf = the SID binary data structure with it's type+counter first
261 *
262 * returns str:
263 * 6 chars athority prefix (S-1-5-)
264 * 4 * 10 digits (the 4 32 bit groups)
265 * 3 for the - between the groups
266 * 1 for null termination
267 * 50 chars
268 */
sam_sid_to_string(struct sid_binary * sidbuf)269 char *sam_sid_to_string(struct sid_binary *sidbuf)
270 {
271
272 int cnt, i;
273 char *str = NULL;
274
275 // hexdump(sidbuf, 0, 24, 1);
276
277
278 if (sidbuf->revision != 1) {
279 fprintf(stderr,"sam_sid_to_string: DEBUG: first byte unexpected: %d\n",sidbuf->revision);
280 }
281
282 cnt = sidbuf->sections;
283
284 // printf("sid_to_string: DEBUG: sections = %d\n",cnt);
285
286 str = str_dup("S-");
287 str = str_catf(str, "%u-%u", sidbuf->revision, sidbuf->authority);
288
289 for (i = 0; i < cnt; i++) {
290 str = str_catf(str,"-%u",sidbuf->array[i]);
291 }
292
293 // printf("sid_to_string: returning <%s>\n",str);
294
295
296 return(str);
297 }
298
299
300
301
302
303 /* Stuff SID binary list into more easily handled arrays
304 * sidbuf = binary list buffer (not changed, may point into value structure)
305 * size = number of bytes of raw data
306 * returns pointer to array, terminated with NULL pointer.
307 * Keeps full binary data from each SID
308 * All array space is allocated, call sam_free_sid_array() to free it.
309 */
310
sam_make_sid_array(struct sid_binary * sidbuf,int size)311 struct sid_array *sam_make_sid_array(struct sid_binary *sidbuf, int size)
312 {
313
314 int num = 0;
315 int sidlen;
316 struct sid_binary *sb;
317 struct sid_array *array;
318
319 CREATE(array, struct sid_array, 1);
320 array[0].len = 0;
321 array[0].sidptr = NULL;
322
323 while (size > 0) {
324
325 sidlen = sidbuf->sections * 4 + 8;
326
327 // printf("make_sid_array: sidlen = %d\n",sidlen);
328
329 ALLOC(sb, 1, sidlen);
330 memcpy(sb, sidbuf, sidlen);
331 array[num].len = sidlen;
332 array[num].sidptr = sb;
333 sidbuf = (void *)sidbuf + sidlen;
334 size -= sidlen;
335 num++;
336
337 array = realloc(array, (num + 1) * sizeof(struct sid_array));
338 array[num].len = 0;
339 array[num].sidptr = NULL;
340
341 }
342
343
344 return(array);
345
346 }
347
348 /* Free the sid array (from the function above) */
349
sam_free_sid_array(struct sid_array * array)350 void sam_free_sid_array(struct sid_array *array)
351 {
352
353 int num = 0;
354
355 while (array[num].sidptr) {
356 free(array[num].sidptr);
357 num++;
358 }
359
360 free(array);
361 }
362
363 /* Compare two SIDs, and return like strcmp */
sam_sid_cmp(struct sid_binary * s1,struct sid_binary * s2)364 int sam_sid_cmp(struct sid_binary *s1, struct sid_binary *s2)
365 {
366 int p;
367
368 if (!s1 && !s2) return(0);
369 if (!s1) return(-1);
370 if (!s2) return(1);
371
372 if (s1->sections < s2->sections) return(-1); /* s1 has shorter len, always smaller */
373 if (s1->sections > s2->sections) return(1); /* s1 has longer len, always larger */
374 /* Run compare since same length */
375 for (p = 0; p < s1->sections; p++) {
376 if (s1->array[p] < s2->array[p]) return (-1);
377 if (s1->array[p] > s2->array[p]) return (1);
378 }
379 /* At end. Thus equal */
380 return(0);
381 }
382
383
384
385
386
387
388 /************** GROUP DATA HANDLING ROUTINES ****************/
389
390
391 /* Get C value of a group ID, searching botg bult-in and user defined
392 * hdesc - hive
393 * grp - group ID
394 * returns pointer to value buffer or NULL if not found
395 */
396
sam_get_grpC(struct hive * hdesc,int grp)397 struct keyval *sam_get_grpC(struct hive *hdesc, int grp)
398 {
399 struct keyval *c = NULL;
400 int n = 0;
401 char g[200];
402
403 /* Try built-in groups first (administrators, user, guests etc) */
404 while (*SAM_GRPCPATHID[n] && !c) {
405 snprintf(g, 180, SAM_GRPCPATHID[n], grp);
406 c = get_val2buf(hdesc, NULL, 0, g, 0, TPF_VK_EXACT);
407 n++;
408 }
409
410 return(c);
411
412 }
413
414
415 /* Get list of group members for a group
416 * Will get the SID list (as binary) into a buffer that will be allocated
417 * according to the neccessary size (based on member count)
418 * NOTE: Caller must free the buffer when not needed any more
419 * grp = group ID
420 * sidarray = pointer to pointer to sid array which will be allocated
421 * Returns number of members in the group
422 */
423
sam_get_grp_members_sid(struct hive * hdesc,int grp,struct sid_array ** sarray)424 int sam_get_grp_members_sid(struct hive *hdesc, int grp, struct sid_array **sarray)
425 {
426 // char groupname[128];
427
428 struct sid_array *marray;
429 struct keyval *c = NULL;
430 struct group_C *cd;
431 // int grpnamoffs, grpnamlen;
432 int mofs, mlen;
433
434 c = sam_get_grpC(hdesc, grp);
435 if (c) {
436 cd = (struct group_C *)&c->data;
437
438 // grpnamoffs = cd->grpname_ofs + 0x34;
439 // grpnamlen = cd->grpname_len;
440
441 // cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen);
442
443 // printf("get_grp_members_sid: group %x named %s has %d members\n",grp,groupname,cd->grp_members);
444
445 mofs = cd->members_ofs;
446 mlen = cd->members_len;
447
448 // printf("get_grp_members_sid: mofs = %x, mlen = %x (%d)\n", mofs,mlen,mlen);
449 // printf("get_grp_members_sid: ajusted: mofs = %x, mlen = %x (%d)\n", mofs + 0x34 ,mlen,mlen);
450
451 // hexdump(&c->data, 0, c->len, 1);
452 // hexdump(&cd->data[mofs], 0, mlen, 1);
453
454 marray = sam_make_sid_array((struct sid_binary *)&cd->data[mofs], mlen);
455
456 *sarray = marray;
457 // sam_free_sid_array(marray);
458
459 free(c);
460
461 } else {
462 printf("Group info for %x not found!\n",grp);
463 *sarray = NULL;
464 return(0);
465 }
466
467 return(cd->grp_members);
468
469 }
470
471 /* Put list of group members back into group C structure
472 * grp = group ID
473 * sidarray = pointer to sid array
474 * Returns true if success
475 */
476
sam_put_grp_members_sid(struct hive * hdesc,int grp,struct sid_array * sarray)477 int sam_put_grp_members_sid(struct hive *hdesc, int grp, struct sid_array *sarray)
478 {
479 char g[200];
480 char groupname[128];
481
482 struct keyval *c = NULL;
483 struct group_C *cd;
484 int grpnamoffs, grpnamlen;
485 int mofs, mlen;
486 int sidlen = 0;
487 void *sidptr;
488 int i, n;
489 char *str;
490
491 /* Try built-in groups first (administrators, user, guests etc) */
492 n = 0;
493 while (*SAM_GRPCPATHID[n] && !c) {
494 snprintf(g, 180, SAM_GRPCPATHID[n], grp);
495 c = get_val2buf(hdesc, NULL, 0, g, 0, TPF_VK_EXACT);
496 n++;
497 }
498
499 if (c) {
500 cd = (struct group_C *)&c->data;
501
502 grpnamoffs = cd->grpname_ofs + 0x34;
503 grpnamlen = cd->grpname_len;
504
505 cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen);
506
507 if (gverbose) printf("put_grp_members_sid: group %x named %s has %d members\n",grp,groupname,cd->grp_members);
508
509 mofs = cd->members_ofs;
510 mlen = cd->members_len;
511
512 if (gverbose) printf("put_grp_members_sid: ajusted: mofs = %x, mlen = %x (%d)\n", mofs + 0x34 ,mlen,mlen);
513
514 if (gverbose) hexdump(&c->data, 0, c->len, 1);
515
516 /* Get total size of new SID data */
517
518 for (i = 0; sarray[i].sidptr; i++) sidlen += sarray[i].len;
519
520 if (gverbose) printf("put_grp_members_sid: new count : %d, new sidlen: %x\n",i,sidlen);
521
522 /* Resize buffer with C structure */
523 c = realloc(c, 4 + mofs + sidlen + 0x34); /* offset of SIDs + sids lenght + pointer list at start */
524 c->len = 0x34 + mofs + sidlen;
525
526 cd = (struct group_C *)&c->data;
527 mofs = cd->members_ofs;
528 sidptr = &cd->data[mofs];
529
530 for (i = 0; sarray[i].sidptr; i++) {
531 if (gverbose) printf(" copying : %d len %x, at %x\n",i,sarray[i].len, sidptr);
532 str = sam_sid_to_string(sarray[i].sidptr);
533 if (gverbose) printf(" Member # %d = <%s>\n", i, str);
534 FREE(str);
535 memcpy(sidptr, sarray[i].sidptr, sarray[i].len);
536 sidptr += sarray[i].len;
537 }
538
539 cd->members_len = sidlen; /* Update member count in C struct */
540 cd->grp_members = i;
541
542 if (gverbose) hexdump(&c->data, 0, c->len, 1);
543
544 if (!put_buf2val(hdesc, c, 0, g, 0, TPF_VK_EXACT)) {
545 fprintf(stderr,"put_grp_members_sid: could not write back group info in value %s\n",g);
546 free(c);
547 return(0);
548 }
549
550
551 free(c);
552
553 } else {
554 printf("Group info for %x not found!\n",grp);
555 return(0);
556 }
557
558 return(1);
559
560 }
561
562
563
564 /* Get group IDs a user is member of
565 * rid = user ID
566 * returns: since value data is just an array of grp ids (4 bytes each),
567 * just return the keyval structure (size + data)
568 * caller must free() keyval
569 */
570
sam_get_user_grpids(struct hive * hdesc,int rid)571 struct keyval *sam_get_user_grpids(struct hive *hdesc, int rid)
572 {
573 char s[200];
574 struct sid_binary sid;
575 char *sidstr;
576
577 int nk = 0;
578 struct keyval *m = NULL;
579 struct keyval *result = NULL;
580 struct keyval *newresult = NULL;
581 int count = 0;
582 int size;
583 int n;
584
585 if (!rid || (hdesc->type != HTYPE_SAM)) return(NULL);
586
587 if (!sam_get_machine_sid(hdesc, (char *)&sid)) {
588 fprintf(stderr,"sam_get_user_grpids: Could not find machine SID\n");
589 return(0);
590 }
591
592 sidstr = sam_sid_to_string(&sid);
593
594 /* Get member list for user on this machine */
595
596 n = 0; /* Look up user RID under computer SID under builtin and account path */
597 while (*SAM_GRPMEMBERSPATH[n]) {
598
599 snprintf(s, 180, SAM_GRPMEMBERSPATH[n], sidstr, rid);
600
601 if (gverbose) printf("sam_get_user_grpids: member path: %s\n",s);
602
603 nk = trav_path(hdesc, 0, s, 0);
604
605 if (nk) { /* Found a key */
606
607 /* Now, the TYPE field is the number of groups the user is member of */
608 /* Don't we just love the inconsistent use of fields!! */
609 nk += 4;
610 count = get_val_type(hdesc,nk,"@",TPF_VK_EXACT);
611 if (count == -1) {
612 printf("sam_get_user_grpids: Cannot find default value <%s\\@>\n",s);
613 n++;
614 continue;
615 }
616
617 // printf("sam_get_user_grpids: User is member of %d groups:\n",count);
618
619 /* This is the data size */
620 size = get_val_len(hdesc,nk,"@",TPF_VK_EXACT);
621
622 /* It should be 4 bytes for each group */
623 // if (gverbose) printf("Data size %d bytes.\n",size);
624 if (size != count * 4) {
625 printf("sam_get_user_grpids: DEBUG: Size is not 4 * count! May not matter anyway. Continuing..\n");
626 }
627
628 m = get_val2buf(hdesc, NULL, nk, "@", 0, TPF_VK_EXACT);
629 if (!m) {
630 printf("sam_get_user_grpids: Could not get value data! Giving up.\n");
631 FREE(sidstr);
632 return(NULL);
633 }
634
635 /* At this point we have a value containing member list from this part of the tree */
636 /* Just append this one to the earlier ones */
637
638 newresult = reg_valcat(result, m);
639 FREE(m);
640 FREE(result);
641 result = newresult;
642
643 }
644
645 n++;
646 }
647
648 FREE(sidstr);
649
650 if (!result) {
651 /* This probably means user is not in any group. Seems to be the case
652 for a couple of XPs built in support / guest users. So just return */
653 if (gverbose) printf("sam_get_user_grpids: Cannot find RID under computer SID <%s>\n",s);
654 return(NULL);
655 }
656
657 // printf(" sam_get_user_grpids done\n");
658 return(result);
659 }
660
661 /* Put/set group IDs a user is member of
662 * rid = user ID
663 * val = keyval structure of data, actual value data is a list
664 * of ints, one per group
665 * returns true if successful setting the value
666 */
667
sam_put_user_grpids(struct hive * hdesc,int rid,struct keyval * val)668 int sam_put_user_grpids(struct hive *hdesc, int rid, struct keyval *val)
669 {
670 char s[200];
671 char news[200];
672 char ks[12];
673 struct nk_key *newkey = NULL;
674 struct sid_binary sid;
675 char *sidstr;
676
677 int n, grp, pnum;
678 int newcount = 0;
679 int nk = 0;
680 int count = 0;
681 struct keyvala *v;
682 struct keyvala *new;
683 struct keyvala entry;
684
685 /* Pointers to value lists for each group path in use */
686 struct keyval *p[SAM_NUM_GROUPPATHS];
687
688 if (!rid || (hdesc->type != HTYPE_SAM)) return(0);
689
690 if (!val) return(0);
691
692 #if 0
693 if (!val->len) {
694 printf("sam_put_user_grpids: zero list len\n");
695 // return(0);
696 }
697 #endif
698
699 v = (struct keyvala *)val;
700
701 if (!sam_get_machine_sid(hdesc, (char *)&sid)) {
702 fprintf(stderr,"sam_put_user_grpids: Could not find machine SID\n");
703 return(0);
704 }
705 sidstr = sam_sid_to_string(&sid);
706
707 for (n = 0; n < SAM_NUM_GROUPPATHS; n++) {
708 ALLOC(p[n], sizeof(struct keyvala), 1);
709 p[n]->len = 0;
710 p[n]->data = 0;
711
712 }
713
714 /* Split value list into relevant stuff for each path */
715 for (n = 0; n < val->len >> 2; n++) {
716 grp = v->data[n];
717 for (pnum = 0; pnum < SAM_NUM_GROUPPATHS; pnum++) {
718 snprintf(s, 180, SAM_GRPCPATHID[pnum], grp);
719 // printf("sam_put_user_grpids: split path: %s\n",s);
720 nk = trav_path(hdesc, 0, s, TPF_VK_EXACT); /* Check if group is in path?? */
721 if (nk) { /* Yup, it is here */
722 entry.data[0] = grp;
723 entry.len = 4;
724 // printf("sam_put_user_grpids: path match for grp ID: %x\n", entry.data[1]);
725 new = (struct keyvala *)reg_valcat( p[pnum], (struct keyval *)&entry);
726 FREE( p[pnum] );
727 p[pnum] = (struct keyval *)new;
728 }
729 }
730 }
731
732
733 /* Now put the lists into the correct place */
734
735 for (n = 0; n < SAM_NUM_GROUPPATHS; n++) {
736
737 /* Get member list for user on this machine */
738 snprintf(s,180,SAM_GRPMEMBERSPATH[n] ,sidstr, rid);
739 // printf("sam_put_user_grpids: putting for path: %s\n",s);
740
741 newcount = p[n]->len >> 2;
742
743 // printf("--- list for that path has len: %d\n",p[n]->len);
744 for (pnum = 0; pnum < p[n]->len >> 2; pnum++) {
745 new = (struct keyvala *)p[n];
746 // printf("%d : %x\n", pnum, new->data[pnum]);
747 }
748
749
750 /* Find users member list under this path */
751
752 nk = trav_path(hdesc, 0, s, 0);
753 if (!nk) {
754 /* User is not in any group in this path, see if we need to create key */
755
756 if (gverbose) printf("sam_put_user_grpids: Cannot find path <%s>\n",s);
757 if (!newcount) continue; /* Nothing to put there anyway, so just try next path */
758
759 snprintf(news,180,SAM_GRPSIDPATH[n] ,sidstr);
760 // snprintf(ks, 180, "%08X", rid);
761
762 // printf("sam_put_user_grpids: creating key <%s> on path <%s>\n",ks,news);
763
764 nk = trav_path(hdesc, 0, news, 0);
765
766 newkey = add_key(hdesc, nk+4, ks);
767 if (!newkey) {
768 fprintf(stderr,"sam_put_user_grpids: ERROR: creating group list key for RID <%08x> under path <%s>\n",rid,news);
769 abort();
770 }
771
772 nk = trav_path(hdesc, 0, s, 0);
773
774 if (!add_value(hdesc, nk+4, "@", 0)) {
775 fprintf(stderr,"sam_put_user_grpids: ERROR: creating group list default value for RID <%08x> under path <%s>\n",rid,news);
776 abort();
777 }
778 }
779
780 nk += 4;
781
782 /* Now, the TYPE field is the number of groups the user is member of */
783
784 count = get_val_type(hdesc, nk,"@", TPF_VK_EXACT);
785 if (count == -1) {
786 printf("sam_put_user_grpids: Cannot find value <%s\\@>\n",s);
787 return(1);
788 }
789
790 if (gverbose) printf("sam_put_user_grpids: User was member of %d groups:\n",count);
791
792 /* This is the data size */
793 /* It should be 4 bytes for each group */
794
795
796 if (gverbose) printf("Data size %d bytes.\n",p[n]->len);
797 if (p[n]->len != newcount << 2) {
798 printf("set_user_grpids: DEBUG: Size is not 4 * count! May not matter anyway. Continuing..\n");
799 }
800
801 if (gverbose) printf("sam_put_user_grpids: User is NOW member of %d groups:\n",newcount);
802
803 if (newcount == 0) { /* Seems windows removes the key and default subvalue when user not in any group */
804
805 // printf("sam_put_user_grpids: removing user reference for path %s\n",s);
806 del_value(hdesc, nk, "@", TPF_VK_EXACT);
807 nk = trav_path(hdesc, nk, "..", 0);
808 snprintf(s,180, "%08X", rid);
809 del_key(hdesc, nk + 4, s);
810
811 } else { /* Stuff back list into default value */
812
813 set_val_type(hdesc, nk, "@", TPF_VK_EXACT, newcount);
814 if (!put_buf2val(hdesc, p[n], nk, "@", 0, TPF_VK_EXACT) ) {
815 printf("sam_put_user_grpids: Could not set reg value data!\n");
816 return(0);
817 }
818
819 }
820
821
822 } /* for path loop */
823
824 FREE(sidstr);
825
826 for (n = 0; n < SAM_NUM_GROUPPATHS; n++) {
827 FREE(p[n]);
828 }
829
830
831 printf("sam_put_user_grpids: success exit\n");
832 return(1);
833
834
835 }
836
837
838
839
840 /********* GROUP / USER MANIPULATION ROUTINES **************/
841
842
843 /* Add user to a group
844 * rid = user RID
845 * grp = group ID
846 * return true if success
847 */
848
sam_add_user_to_grp(struct hive * hdesc,int rid,int grp)849 int sam_add_user_to_grp(struct hive *hdesc, int rid, int grp)
850 {
851 struct keyvala *usrgrplist, *newusrgrplist;
852 struct sid_array *sarray, *narray;
853 struct sid_binary *usid;
854 struct sid_binary msid;
855 int members, newmembers;
856 char *str;
857 int ugcnt;
858 int o,n,hit,c;
859 unsigned int *og, *ng;
860 char s[200];
861
862
863 if (!rid || !grp || (hdesc->type !=HTYPE_SAM) ) return(0);
864
865 snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\V",rid);
866 if (!trav_path(hdesc, 0, s, TPF_VK_EXACT)) {
867 fprintf(stderr,"sam_add_user_to_grp: user # %x not found!\n",rid);
868 return(0);
869 }
870
871 /* Build user SID (add RID to machine SID) */
872
873 if (!sam_get_machine_sid(hdesc, (char *)&msid)) {
874 fprintf(stderr,"sam_add_user_to_grp: Could not find machine SID\n");
875 return(0);
876 }
877
878 /* well, and hope that machine SID is always same size here too */
879 ALLOC(usid, sizeof(struct sid_binary) +4, 1);
880
881 memcpy(usid, &msid, sizeof(struct sid_binary));
882
883 usid->array[4] = rid; /* Tack RID on at end */
884 usid->sections = 5;
885
886 str = sam_sid_to_string(usid);
887
888 if (gverbose) printf("add_user_to_grp: user SID is <%s>\n", str);
889
890 free(str);
891
892 /* With all of the above functions, it should now just be to get
893 * the list of groups the user account has listed under it
894 * and the list of users the group has listed under it
895 */
896
897 usrgrplist = (struct keyvala *)sam_get_user_grpids(hdesc, rid);
898
899 if (!usrgrplist) {
900 printf("sam_add_user_to_grp: user # %x WAS IN NO GROUPS!\n",rid);
901 /* So make new blank list for it */
902 ALLOC(usrgrplist, sizeof(struct keyvala), 1);
903 usrgrplist->len = 0;
904 usrgrplist->data[0] = 0;
905 }
906
907
908 members = sam_get_grp_members_sid(hdesc, grp, &sarray);
909
910 if (!sarray) {
911 printf("sam_add_user_to_grp: group # %x not found!\n",grp);
912 FREE(usrgrplist);
913 return(0);
914 }
915
916
917 /* Add the group to the users list of groups it is member of */
918
919 ugcnt = usrgrplist->len >> 2; /* Count of groups already on user */
920
921 /* Allocate new larger usrgrplist for one more entry */
922
923 ALLOC(newusrgrplist, usrgrplist->len + 4 + 4, 1);
924 bzero(newusrgrplist, usrgrplist->len + 4 + 4); /* for sanity.. */
925 newusrgrplist->len = usrgrplist->len + 4;
926
927 og = (unsigned int *)&usrgrplist->data;
928 ng = (unsigned int *)&newusrgrplist->data;
929
930 if (gverbose) printf("usrgrplist-len = %d\n", usrgrplist->len);
931
932
933 #if 0 /* If list should be sorted, but seems windows does not do that? */
934
935 /* Copy over users group list, adding in where needed */
936
937 hit = 0;
938 for (o = 0, n = 0; o < ugcnt; o++, n++) {
939 printf(":: %d %d : %x\n",o,n,og[o]);
940 if (og[o] == grp) { /* Was already in there, so just don't increase size.. */
941 newusrgrplist->len = usrgrplist->len;
942 hit = 1;
943 }
944 if (og[o] > grp && !hit) {
945 ng[n++] = grp; /* Next is higher, so insert out rid */
946 hit = 1;
947 printf(" -- insert\n");
948 }
949 ng[n] = og[o];
950 }
951 printf("n = %d\n",n);
952 if (!hit) ng[n] = grp; /* Insert at end if we run down */
953
954 #endif
955
956 /* Copy over users group list, checking if already there */
957
958 hit = 0;
959 for (o = 0; o < ugcnt; o++) {
960 if (gverbose) printf(":: %d : %x\n",o,og[o]);
961 if (og[o] == grp) { /* Was already in there, so just don't increase size.. */
962 newusrgrplist->len = usrgrplist->len;
963 hit = 1;
964 if (gverbose) printf(" -- match\n");
965 }
966 ng[o] = og[o];
967 }
968 if (gverbose) printf(" - end of list at o = %d\n",o);
969
970 if (!hit) ng[o] = grp; /* Just stuff new group in at end if not already in list */
971
972 if (gverbose) {
973 for (o = 0; o < (newusrgrplist->len >> 2); o++) {
974 printf("grp index %d = %08x\n", o, ng[o]);
975 }
976 }
977 /* And then we add the user SID into the groups list of members */
978
979 if (gverbose) {
980 printf("add_user_to_grp: grp memberlist BEFORE:\n");
981
982 for (o = 0; sarray[o].sidptr; o++) {
983 str = sam_sid_to_string(sarray[o].sidptr);
984 printf(" Member # %d = <%s>\n", o, str);
985 FREE(str);
986 }
987 }
988
989 newmembers = members + 1;
990 ALLOC(narray, sizeof(struct sid_array) * (newmembers + 2), 1); /* Add one entry size */
991
992 if (gverbose) printf("members = %d\n", members);
993
994 hit = 0;
995 for (o = 0, n = 0; o <= members; o++, n++) {
996 c = sam_sid_cmp(sarray[o].sidptr, usid); /* Compare slot with new SID */
997 if (gverbose) printf("sam_sid_cmp returns %d\n",c);
998 if (c == 0) {
999 newmembers--; /* Already there, don't change anything */
1000 hit = 1;
1001 }
1002 if (!hit && ((c > 0) || !sarray[o].sidptr)) { /* Next is higher, insert new SID */
1003 if (gverbose) printf(" -- add\n");
1004 narray[n].len = usid->sections * 4 + 8; /* Hmm */
1005 narray[n].sidptr = usid;
1006 n++;
1007 hit = 1;
1008 }
1009 narray[n].len = sarray[o].len;
1010 narray[n].sidptr = sarray[o].sidptr;
1011 }
1012
1013 if (gverbose) {
1014 printf("add_user_to_grp: grp memberlist AFTER:\n");
1015
1016
1017 for (o = 0; narray[o].sidptr; o++) {
1018 str = sam_sid_to_string(narray[o].sidptr);
1019 printf(" Member # %d = <%s>\n", o, str);
1020 FREE(str);
1021 }
1022 }
1023
1024 /* Write new lists back to registry */
1025 if (!sam_put_user_grpids(hdesc, rid, (struct keyval *)newusrgrplist)) {
1026 fprintf(stderr, "add_user_to_grp: failed storing users group list\n");
1027 } else if (!sam_put_grp_members_sid(hdesc, grp, narray)) {
1028 fprintf(stderr,"add_user_to_grp: failed storing groups user list\n");
1029 sam_put_user_grpids(hdesc, rid, (struct keyval *)usrgrplist); /* Try to roll back */
1030 }
1031
1032 FREE(usrgrplist);
1033 FREE(newusrgrplist);
1034 sam_free_sid_array(narray);
1035 FREE(sarray); /* Pointers was copied to narray, and freed above, just free the array here */
1036
1037 return(1);
1038
1039 }
1040
1041 /* Remove user from a group
1042 * rid = user RID
1043 * grp = group ID
1044 * return true if success
1045 */
1046
sam_remove_user_from_grp(struct hive * hdesc,int rid,int grp)1047 int sam_remove_user_from_grp(struct hive *hdesc, int rid, int grp)
1048 {
1049 struct keyvala *usrgrplist, *newusrgrplist;
1050 struct sid_array *sarray, *narray;
1051 struct sid_binary *usid;
1052 struct sid_binary msid;
1053 int members, newmembers;
1054 char *str;
1055 int ugcnt;
1056 int o,n,hit,c;
1057 unsigned int *og, *ng;
1058
1059
1060
1061 if (!rid || !grp || (hdesc->type != HTYPE_SAM)) return(0);
1062
1063 /* Build user SID (add RID to machine SID) */
1064
1065 if (!sam_get_machine_sid(hdesc, (char *)&msid)) {
1066 fprintf(stderr,"sam_remove_user_from_grp: Could not find machine SID\n");
1067 return(0);
1068 }
1069
1070 /* well, and hope that machine SID is always same size here too */
1071 ALLOC(usid, sizeof(struct sid_binary) +4, 1);
1072
1073 memcpy(usid, &msid, sizeof(struct sid_binary));
1074
1075 usid->array[4] = rid; /* Tack RID on at end */
1076 usid->sections = 5;
1077
1078 if (gverbose) {
1079 str = sam_sid_to_string(usid);
1080 printf("remove_user_from_grp: user SID is <%s>\n", str);
1081 free(str);
1082 }
1083
1084 /* With all of the above functions, it should now just be to get
1085 * the list of groups the user account has listed under it
1086 * and the list of users the group has listed under it
1087 */
1088
1089 usrgrplist = (struct keyvala *)sam_get_user_grpids(hdesc, rid);
1090
1091 if (!usrgrplist) {
1092 printf("remove_user_from_grp: user # %x not found!\n",rid);
1093 return(0);
1094 }
1095
1096
1097 members = sam_get_grp_members_sid(hdesc, grp, &sarray);
1098
1099 if (!sarray) {
1100 printf("remove_user_from_grp: group # %x not found!\n",grp);
1101 FREE(usrgrplist);
1102 return(0);
1103 }
1104
1105
1106 /* Add the group to the users list of groups it is member of */
1107
1108 ugcnt = usrgrplist->len >> 2; /* Count of groups already on user */
1109
1110 /* Allocate same size usrgrplist, since we don't know if we are in there and need to be removed */
1111
1112 ALLOC(newusrgrplist, usrgrplist->len + 4, 1);
1113 bzero(newusrgrplist, usrgrplist->len + 4); /* for sanity.. */
1114 newusrgrplist->len = usrgrplist->len;
1115
1116 og = (unsigned int *)&usrgrplist->data;
1117 ng = (unsigned int *)&newusrgrplist->data;
1118
1119 if (gverbose) printf("usrgrplist-len = %d\n", usrgrplist->len);
1120
1121
1122 /* Copy over users group list, if relevant group found, don't copy it over */
1123
1124 hit = 0;
1125 for (o = 0; o < ugcnt; o++) {
1126 if (gverbose) printf(":: %d : %x\n",o,og[o]);
1127 if (og[o] == grp) { /* Group found */
1128 hit = 1;
1129 if (gverbose) printf(" -- match\n");
1130 } else {
1131 ng[o-hit] = og[o];
1132 }
1133 }
1134 if (gverbose) printf(" - end of list at o = %d\n",o);
1135 if (hit) {
1136 newusrgrplist->len -= 4; /* Decrease size if found */
1137 } else {
1138 fprintf(stderr, "remove_user_from_grp: NOTE: group not in users list of groups, may mean user not member at all. Safe. Continuing.\n");
1139 }
1140
1141
1142 if (gverbose) {
1143 for (o = 0; o < (newusrgrplist->len >> 2); o++) {
1144 printf("grp index %d = %08x\n", o, ng[o]);
1145 }
1146
1147 /* Remove the user SID from the groups list of members */
1148
1149 printf("remove_user_from_grp: grp memberlist BEFORE:\n");
1150
1151 for (o = 0; sarray[o].sidptr; o++) {
1152 str = sam_sid_to_string(sarray[o].sidptr);
1153 printf(" Member # %d = <%s>\n", o, str);
1154 FREE(str);
1155 }
1156 }
1157
1158 newmembers = members;
1159 ALLOC(narray, sizeof(struct sid_array) * (newmembers + 2), 1);
1160
1161 if (gverbose) printf("members = %d\n", members);
1162
1163
1164 hit = 0;
1165 for (o = 0, n = 0; o <= members; o++, n++) {
1166 c = sam_sid_cmp(sarray[o].sidptr, usid); /* Compare slot with new SID */
1167 if (gverbose) printf("sid_cmp returns %d\n",c);
1168 if (c == 0) {
1169 newmembers--; /* Found, skip copy and decrease list size */
1170 hit = 1;
1171 n--;
1172 } else {
1173 narray[n].len = sarray[o].len; /* Copy entry */
1174 narray[n].sidptr = sarray[o].sidptr;
1175 }
1176 }
1177 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");
1178
1179 if (gverbose) {
1180 printf("remove_user_from_grp: grp memberlist AFTER:\n");
1181 for (o = 0; narray[o].sidptr; o++) {
1182 str = sam_sid_to_string(narray[o].sidptr);
1183 printf(" Member # %d = <%s>\n", o, str);
1184 FREE(str);
1185 }
1186 }
1187
1188 /* Write new lists back to registry */
1189 if (!sam_put_user_grpids(hdesc, rid, (struct keyval *)newusrgrplist)) {
1190 fprintf(stderr, "remove_user_from_grp: failed storing users group list\n");
1191 } else if (!sam_put_grp_members_sid(hdesc, grp, narray)) {
1192 fprintf(stderr,"remvoe_user_from_grp: failed storing groups user list\n");
1193 sam_put_user_grpids(hdesc, rid, (struct keyval *)usrgrplist); /* Try to roll back */
1194 }
1195
1196 FREE(usrgrplist);
1197 FREE(newusrgrplist);
1198 sam_free_sid_array(narray);
1199 FREE(sarray); /* Pointers was copied to narray, and freed above, just free the array here */
1200
1201 return(1);
1202
1203 }
1204
1205
1206 /* TODO: So.. having listing functions in library.. should better be handled by tools.. */
1207
1208 /* List users membership or check if admin (is in admin group)
1209 * rid - users rid
1210 * check - if 1 just check if admin, do not list
1211 * returns true if user is admin
1212 */
1213
sam_list_user_groups(struct hive * hdesc,int rid,int check)1214 int sam_list_user_groups(struct hive *hdesc, int rid, int check)
1215 {
1216 char groupname[128];
1217 struct keyval *m = NULL, *c = NULL;
1218 struct group_C *cd;
1219 unsigned int *grps;
1220 int count = 0, isadmin = 0;
1221 int i, grp, grpnamoffs, grpnamlen;
1222
1223 if (!rid || (hdesc->type != HTYPE_SAM) ) return(0);
1224
1225 m = sam_get_user_grpids(hdesc, rid);
1226
1227 if (!m) return(0);
1228
1229 grps = (unsigned int *)&m->data;
1230 count = m->len >> 2;
1231
1232 for (i = 0; i < count; i++) {
1233 grp = grps[i];
1234 if (!check) printf("%08x ",grp);
1235
1236 if (grp == 0x220) isadmin = 1;
1237
1238 if (!check) {
1239 c = sam_get_grpC(hdesc, grp);
1240 if (c) {
1241 cd = (struct group_C *)&c->data;
1242 grpnamoffs = cd->grpname_ofs + 0x34;
1243 grpnamlen = cd->grpname_len;
1244
1245 cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen);
1246
1247 printf("= %s (which has %d members)\n",groupname,cd->grp_members);
1248
1249 // get_grp_members_sid(grp, &sidbuf);
1250
1251 } else {
1252 printf("Group info for %x not found!\n",grp);
1253 }
1254 }
1255 }
1256
1257 free(m);
1258
1259 return(isadmin);
1260 }
1261
1262
1263
1264
1265 /* List users in SAM file
1266 * readable - 1 = list in human readable form, 0 = colon-separated, 2 = quiet, no ouput (find admin)
1267 * return logic:
1268 * If no users / error: 0
1269 * If only 0x1f4 (built-in adminsitrator) is admin (or no one at all in admin group), return 0x1f4
1270 * Else return lowest numbered user that is in admin group
1271 *
1272 * Fields ouput in parsable listing (all numbers in hex)
1273 * rid:username:isadmin:acb:hashlen
1274 * rid = User RID
1275 * isadmin = (boolean flag) 1 user is admin, 0 is not
1276 * acb = ACB account bits
1277 * hashlen = lenght of password hash, 14 is normal if has passwd, 4 if blank
1278 */
1279
1280 char SAMdaunPATH[] = "\\SAM\\Domains\\Account\\Users\\Names\\";
1281
sam_list_users(struct hive * hdesc,int readable)1282 int sam_list_users(struct hive *hdesc, int readable)
1283 {
1284 char s[200];
1285 struct keyval *v;
1286 int nkofs /* ,vkofs */ ;
1287 int rid;
1288 int count = 0, countri = 0;
1289 int ntpw_len;
1290
1291 int admrid = 0x1f4;
1292 int isadm;
1293
1294 unsigned short acb;
1295
1296 struct user_V *vpwd;
1297 struct ex_data ex;
1298
1299 if (hdesc->type != HTYPE_SAM) return(0);
1300
1301 nkofs = trav_path(hdesc, 0, SAMdaunPATH, 0);
1302 if (!nkofs) {
1303 printf("sam_list_users: Cannot find usernames in registry! (is this a SAM-hive?)\n");
1304 return(0);
1305 }
1306
1307 if (readable == 1) printf("| RID -|---------- Username ------------| Admin? |- Lock? --|\n");
1308
1309 while ((ex_next_n(hdesc, nkofs+4, &count, &countri, &ex) > 0)) {
1310
1311 /* Extract the value out of the username-key, value is RID */
1312 snprintf(s,180,"%s%s\\@",SAMdaunPATH, ex.name);
1313 rid = get_dword(hdesc, 0, s, TPF_VK_EXACT|TPF_VK_SHORT);
1314
1315 /* Now that we have the RID, build the path to, and get the V-value */
1316 snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\V",rid);
1317 v = get_val2buf(hdesc, NULL, 0, s, REG_BINARY, TPF_VK_EXACT);
1318 if (!v) {
1319 printf("Cannot find value <%s>\n",s);
1320 return(1);
1321 }
1322
1323 if (v->len < 0xcc) {
1324 printf("sam_list_users: Value <%s> is too short (only %d bytes) to be a SAM user V-struct!\n",
1325 s, v->len);
1326 } else {
1327
1328 vpwd = (struct user_V *)&(v->data);
1329 ntpw_len = vpwd->ntpw_len;
1330
1331 acb = sam_handle_accountbits(hdesc, rid, 0);
1332 isadm = sam_list_user_groups(hdesc, rid, 1);
1333
1334 if (isadm && rid != 0x1f4) { /* Found an non-built-in administrator */
1335 if (admrid == 0x1f4) admrid = rid; /* Prefer anything over built-in one */
1336 if (rid < admrid) admrid = rid;
1337 }
1338
1339 if (readable == 1) {
1340 printf("| %04x | %-30.30s | %-6s | %-8s |\n",
1341 rid, ex.name, ( isadm ? "ADMIN" : "") , ( acb & 0x8000 ? "dis/lock" : (ntpw_len < 16) ? "*BLANK*" : "") );
1342 } else if (readable == 0) {
1343 printf("%04x:%s:%d:%x:%x\n",
1344 rid, ex.name, isadm , acb, ntpw_len );
1345 }
1346
1347
1348 // change_pw( (char *)&v->data , rid, v->len, (*automode == 'l') ? 2 : 1);
1349
1350 }
1351 FREE(v);
1352 FREE(ex.name);
1353 }
1354 return(admrid);
1355 }
1356
1357
1358
1359 /* Get username when we have a RID
1360 * hdesc - hive
1361 * rid - just that.. :)
1362 * returns allocated string with username, caller must free it
1363 * or NULL if RID not found in local databse
1364 */
1365
sam_get_username(struct hive * hdesc,int rid)1366 char *sam_get_username(struct hive *hdesc, int rid)
1367 {
1368 char s[200];
1369 char *username = NULL;
1370 int username_offset,username_len;
1371 struct user_V *v;
1372 struct keyval *value;
1373 int vlen;
1374 char *vp;
1375
1376 snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\V",rid);
1377 value = get_val2buf(hdesc, NULL, 0, s, REG_BINARY, TPF_VK_EXACT);
1378 if (!value) {
1379 printf(" sam_get_username: ERROR: User with RID 0x%x not found, path <%s>\n",rid,s);
1380 return(NULL);
1381 }
1382
1383 vlen = value->len;
1384 if (vlen < 0xcc) {
1385 printf(" sam_get_username: Value <%s> is too short (only %d bytes) to be a SAM user V-struct!\n",
1386 s, vlen);
1387 FREE(value);
1388 return(NULL);
1389 }
1390
1391 v = (struct user_V *)&value->data;
1392 vp = (char *)&value->data;
1393
1394 username_offset = v->username_ofs;
1395 username_len = v->username_len;
1396
1397 if(username_len <= 0 || username_len > vlen ||
1398 username_offset <= 0 || username_offset >= vlen)
1399 {
1400 printf(" sam_get_username: Not a legal V struct? (negative struct lengths)\n");
1401 FREE(value);
1402 return(0);
1403 }
1404
1405 /* Offsets in top of struct is relative to end of pointers, adjust */
1406 username_offset += 0xCC;
1407
1408 ALLOC(username, 2, (username_len >> 1) + 4);
1409 *username = 0;
1410 cheap_uni2ascii(vp + username_offset,username,username_len);
1411
1412 if (gverbose) {
1413 printf("RID : %04d [%04x]\n",rid,rid);
1414 printf("Username: %s\n",username);
1415 }
1416
1417 FREE(value);
1418 return(username);
1419
1420 }
1421
1422
1423
1424
1425 /* Get username from SID:
1426 * Local database if matching machine SID
1427 * Well known SIDs if in list
1428 * Else probably domain SID, don't know how to find more info
1429 *
1430 * sid = sid to extract RID from
1431 *
1432 * returns a string (which must be free()d) or NULL if not able to resolve
1433 */
1434
sam_get_username_from_sid(struct hive * hdesc,struct sid_binary * sid)1435 char *sam_get_username_from_sid(struct hive *hdesc, struct sid_binary *sid)
1436 {
1437 int rid;
1438 char *str;
1439 int i;
1440 struct sid_binary msid;
1441
1442 struct known_sidentry {
1443 int val;
1444 char *name;
1445 };
1446
1447 const struct known_sidentry ntauthority_table[] = {
1448 { 4, "INTERACTIVE" },
1449 { 11, "Authenticated Users" },
1450 { 17, "IUSR" },
1451 { 20, "NETWORK SERVICE" },
1452 { 0, "" }
1453 };
1454
1455
1456 if (!sid) return(NULL);
1457 if(sid->sections < 1) return(NULL);
1458
1459 rid = sid->array[sid->sections-1];
1460
1461 if (sid->sections != 5) {
1462 // fprintf(stderr," WARNING: sam_get_rid_from_sid: Strange size SID, sections = %d, not 5, got rid = %d\n",sid->sections, rid);
1463
1464 if (sid->sections == 1) {
1465 if (sid->authority == 5) { /* We only handle S-1-5 (NTAUTHORITY) known names yet */
1466 str = str_dup("NT AUTHORITY\\");
1467 for (i = 0; ntauthority_table[i].val; i++) {
1468 if (rid == ntauthority_table[i].val) {
1469 str = str_cat(str, ntauthority_table[i].name);
1470 return(str);
1471 }
1472
1473 }
1474
1475 } else { /* Not NT AUTHORITY */
1476 return(sam_sid_to_string(sid));
1477 }
1478 } /* sections */
1479 return(sam_sid_to_string(sid));
1480 }
1481
1482
1483 if (sam_get_machine_sid(hdesc, (char *)&msid)) {
1484
1485 sid->sections--; /* Don't compare RID part */
1486 if (!sam_sid_cmp(sid, &msid)) {
1487 sid->sections++;
1488 return(sam_get_username(hdesc, rid)); /* Match, find and return local username */
1489 } else {
1490 sid->sections++;
1491 return(sam_sid_to_string(sid)); /* No match with local machine SID, so just return SID string */
1492 }
1493
1494 }
1495
1496 /* If we get here we don't have a machine SID, so, well, try to get a local name anyway */
1497 return(sam_get_username(hdesc, rid));
1498
1499 }
1500
1501
1502 /* List groups in SAM
1503 * hdesc - the hive
1504 * listmembers - true = list groups members also, else just group id/name etc
1505 * human = human readable form (true) or parsable (false)
1506 *
1507 * Format of group list:
1508 * grpid:grpname:membercount
1509 * Format of membership list:
1510 * grpid:grpname:index:rid:username:usersid
1511 * where index is just the ordinal number in the groups membership list (starts at 0)
1512 * grpid and rid is in hex
1513 */
1514
sam_list_groups(struct hive * hdesc,int listmembers,int human)1515 void sam_list_groups(struct hive *hdesc, int listmembers, int human) {
1516
1517 struct ex_data ex;
1518 struct sid_array *sids = NULL;
1519 int nkofs;
1520 unsigned int grp;
1521 int count,countri;
1522 struct keyval *c = NULL;
1523 struct group_C *cd;
1524 int grpnamoffs, grpnamlen, i;
1525 char groupname[200];
1526 char *str;
1527 char *username;
1528 int pnum = 0;
1529
1530 if (hdesc->type != HTYPE_SAM) return;
1531
1532 while (*SAM_GRPCPATHS[pnum]) {
1533
1534 // printf(" -- grp C list path: %s\n",SAM_GRPCPATHS[pnum]);
1535
1536 nkofs = trav_path(hdesc, 0, SAM_GRPCPATHS[pnum], 0);
1537 if (!nkofs) {
1538 printf(" list_groups: Cannot find group list in registry! (is this a SAM-hive?)\n");
1539 return;
1540 }
1541
1542 /* Pick up all subkeys here, they are local groups */
1543 count = 0;
1544 countri = 0;
1545 while ((ex_next_n(hdesc, nkofs+4, &count, &countri, &ex) > 0)) {
1546
1547 // printf("Group ID %s\n",ex.name);
1548 sscanf(ex.name,"%x",&grp);
1549
1550 /* Groups keys have a C value, get it and pick up the name etc */
1551 /* Some other keys also exists (Members, Names at least), but we skip them */
1552
1553 c = get_val2buf(hdesc, NULL, ex.nkoffs+4, "C", 0, TPF_VK_EXACT);
1554 if (c) {
1555 cd = (struct group_C *)&c->data;
1556 grpnamoffs = cd->grpname_ofs + 0x34;
1557 grpnamlen = cd->grpname_len;
1558
1559 cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen);
1560
1561 if (human) printf("=== Group #%4x : %s\n",grp,groupname);
1562 else if (!listmembers) printf("%x:%s:%d\n",grp,groupname,cd->grp_members);
1563
1564 if (listmembers) {
1565 sam_get_grp_members_sid(hdesc, grp, &sids);
1566
1567 for (i = 0; sids[i].sidptr; i++) {
1568 str = sam_sid_to_string(sids[i].sidptr);
1569 username = sam_get_username_from_sid(hdesc, sids[i].sidptr);
1570 if (human) printf(" %3d | %04x | %-31s | <%s>\n", i, sids[i].sidptr->array[sids[i].sidptr->sections-1], username, str);
1571 else printf("%x:%s:%d:%x:%s:%s\n", grp, groupname, i, sids[i].sidptr->array[sids[i].sidptr->sections-1], username, str);
1572
1573 FREE(username);
1574 FREE(str);
1575
1576 }
1577 sam_free_sid_array(sids);
1578 }
1579 } /* if c */
1580
1581 }
1582
1583 pnum++;
1584 } /* path loop */
1585 }
1586
1587 /* Get groupname when we have a group ID
1588 * hdesc - hive
1589 * grpid - just that.. :)
1590 * returns allocated string with username, caller must free it
1591 * or NULL if RID not found in local databse
1592 */
1593
sam_get_groupname(struct hive * hdesc,int grpid)1594 char *sam_get_groupname(struct hive *hdesc, int grpid)
1595 {
1596
1597 struct keyval *value = NULL;
1598 struct group_C *cd;
1599 int grpnamoffs;
1600 int grpnamlen;
1601 char *groupname = NULL;
1602
1603
1604 value = sam_get_grpC(hdesc, grpid);
1605 if (!value) {
1606 printf(" sam_get_groupname: ERROR: Group ID 0x%x not found\n",grpid);
1607 return(NULL);
1608 }
1609
1610
1611 /* Offsets in top of struct is relative to end of pointers, adjust */
1612
1613 cd = (struct group_C *)&value->data;
1614 grpnamoffs = cd->grpname_ofs + 0x34;
1615 grpnamlen = cd->grpname_len;
1616
1617 ALLOC(groupname, 2, (grpnamlen >> 1) + 4);
1618 *groupname = 0;
1619 cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen);
1620
1621 // printf("==== Group #%4x : %s\n",grpid,groupname);
1622
1623 FREE(value);
1624 return(groupname);
1625
1626 }
1627
1628
1629
1630
1631
1632 /* Reset users password
1633 * hdesc - the HIVE :)
1634 * rid - the users RID
1635 *
1636 * Returns: 0 = OK, 1 = error (for use in exit())
1637 */
sam_reset_pw(struct hive * hdesc,int rid)1638 int sam_reset_pw(struct hive *hdesc, int rid)
1639 {
1640
1641 char *vp;
1642 static char username[128],fullname[128];
1643 int username_offset,username_len;
1644 int fullname_offset,fullname_len;
1645 int ntpw_len,lmpw_len,ntpw_offs,lmpw_offs;
1646 int vlen;
1647 struct user_V *v;
1648 struct keyval *value;
1649 char s[200];
1650
1651 if (!hdesc || !rid) return(1);
1652
1653 /* Now that we have the RID, build the path to, and get the V-value */
1654 snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\V",rid);
1655 value = get_val2buf(hdesc, NULL, 0, s, REG_BINARY, TPF_VK_EXACT);
1656 if (!value) {
1657 printf(" sam_reset_pw: ERROR: User with RID 0x%x not found, path <%s>\n",rid,s);
1658 return(1);
1659 }
1660
1661 vlen = value->len;
1662 if (vlen < 0xcc) {
1663 printf(" sam_reset_pw: Value <%s> is too short (only %d bytes) to be a SAM user V-struct!\n",
1664 s, vlen);
1665 return(1);
1666 }
1667
1668 v = (struct user_V *)&value->data;
1669 vp = (char *)&value->data;
1670
1671 username_offset = v->username_ofs;
1672 username_len = v->username_len;
1673 fullname_offset = v->fullname_ofs;
1674 fullname_len = v->fullname_len;
1675 lmpw_offs = v->lmpw_ofs;
1676 lmpw_len = v->lmpw_len;
1677 ntpw_offs = v->ntpw_ofs;
1678 ntpw_len = v->ntpw_len;
1679
1680 if (gverbose) {
1681 printf(" lmpw_offs: 0x%x, lmpw_len: %d (0x%x)\n",lmpw_offs,lmpw_len,lmpw_len);
1682 printf(" ntpw_offs: 0x%x, ntpw_len: %d (0x%x)\n",ntpw_offs,ntpw_len,ntpw_len);
1683 }
1684
1685 *username = 0;
1686 *fullname = 0;
1687
1688 if(username_len <= 0 || username_len > vlen ||
1689 username_offset <= 0 || username_offset >= vlen ||
1690 fullname_len < 0 || fullname_len > vlen ||
1691 lmpw_offs < 0 || lmpw_offs >= vlen)
1692 {
1693 printf(" sam_reset_pw: Not a legal V struct? (negative struct lengths)\n");
1694 FREE(value);
1695 return(0);
1696 }
1697
1698 /* Offsets in top of struct is relative to end of pointers, adjust */
1699 username_offset += 0xCC;
1700 fullname_offset += 0xCC;
1701 ntpw_offs += 0xCC;
1702 lmpw_offs += 0xCC;
1703
1704 cheap_uni2ascii(vp + username_offset,username,username_len);
1705 cheap_uni2ascii(vp + fullname_offset,fullname,fullname_len);
1706
1707 if (gverbose) {
1708 printf("RID : %04d [%04x]\n",rid,rid);
1709 printf("Username: %s\n",username);
1710 printf("fullname: %s\n",fullname);
1711 }
1712
1713 /* Setting hash lengths to zero seems to make NT think it is blank
1714 * However, we probably leak about 40 bytes since I am to lazy to adjust the rest
1715 * of the V structure.
1716 */
1717 v->ntpw_len = 0;
1718 v->lmpw_len = 0;
1719
1720 if (!(put_buf2val(hdesc, value, 0, s, REG_BINARY, TPF_VK_EXACT))) {
1721 printf(" reset_pw: Failed to write updated <%s> to registry! Password change not completed!\n",s);
1722 FREE(value);
1723 return(1);
1724 }
1725
1726 if (gverbose) printf(" reset_pw: Password cleared for user %s\n",username);
1727 FREE(value);
1728 return(0);
1729
1730 }
1731
1732
1733 /* Reset password of ALL admin users
1734 * hdesc - hive
1735 * list - if true, list some info about users processed
1736 */
1737
sam_reset_all_pw(struct hive * hdesc,int list)1738 int sam_reset_all_pw(struct hive *hdesc, int list)
1739 {
1740 char s[200];
1741 struct keyval *v;
1742 int nkofs;
1743 int rid;
1744 int isadm;
1745 int count = 0;
1746 int countri = 0;
1747 int fail = 0;
1748
1749 struct ex_data ex;
1750
1751 if (hdesc->type != HTYPE_SAM) return(0);
1752
1753 nkofs = trav_path(hdesc, 0, SAMdaunPATH, 0);
1754 if (!nkofs) {
1755 printf("sam_reset_all_pw: Cannot find usernames in registry! (is this a SAM-hive?)\n");
1756 return(1);
1757 }
1758
1759 while ((ex_next_n(hdesc, nkofs+4, &count, &countri, &ex) > 0)) {
1760
1761 /* Extract the value out of the username-key, value is RID */
1762 snprintf(s,180,"%s%s\\@",SAMdaunPATH, ex.name);
1763 rid = get_dword(hdesc, 0, s, TPF_VK_EXACT|TPF_VK_SHORT);
1764
1765 /* Now that we have the RID, build the path to, and get the V-value */
1766 snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\V",rid);
1767 v = get_val2buf(hdesc, NULL, 0, s, REG_BINARY, TPF_VK_EXACT);
1768 if (!v) {
1769 printf("sam_reset_all_pw: Cannot find value <%s>\n",s);
1770 return(1);
1771 }
1772
1773 if (v->len < 0xcc) {
1774 printf("sam_reset_all_pw: Value <%s> is too short (only %d bytes) to be a SAM user V-struct!\n",
1775 s, v->len);
1776 } else {
1777
1778 isadm = sam_list_user_groups(hdesc, rid, 1);
1779
1780 if (isadm) {
1781 if (list) printf("Reset user :%04x:%s\n", rid, ex.name );
1782 fail |= sam_reset_pw(hdesc, rid);
1783 }
1784
1785
1786 }
1787 FREE(v);
1788 FREE(ex.name);
1789 }
1790 return(fail);
1791 }
1792