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