Given Jeremy's positive response, and a lack of one from tpot, I'll commit
[kai/samba.git] / source3 / rpcclient / samsync.c
1 /* 
2    Unix SMB/CIFS implementation.
3    RPC pipe client
4
5    Copyright (C) Tim Potter 2001
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 static void decode_domain_info(SAM_DOMAIN_INFO *a)
25 {
26         fstring temp;
27         printf("Domain Information\n");
28         printf("------------------\n");
29
30         unistr2_to_ascii(temp, &a->uni_dom_name, sizeof(temp)-1);
31         printf("\tDomain              :%s\n", temp);
32         printf("\tMin password len    :%d\n", a->min_pwd_len);
33         printf("\tpassword history len:%d\n", a->pwd_history_len);
34         printf("\tcreation time       :%s\n", http_timestring(nt_time_to_unix(&a->creation_time)));
35 }
36
37 static void decode_sam_group_info(SAM_GROUP_INFO *a)
38 {
39         fstring temp;
40         printf("\nDomain Group Information\n");
41         printf("------------------------\n");
42
43         unistr2_to_ascii(temp, &a->uni_grp_name, sizeof(temp)-1);
44         printf("\tGroup name          :%s\n", temp);
45         unistr2_to_ascii(temp, &a->uni_grp_desc, sizeof(temp)-1);
46         printf("\tGroup description   :%s\n", temp);
47         printf("\trid                 :%d\n", a->gid.g_rid);
48         printf("\tattribute           :%d\n", a->gid.attr);
49 }
50
51 static void decode_sam_account_info(SAM_ACCOUNT_INFO *a)
52 {
53         fstring temp;
54         printf("\nUser Information\n");
55         printf("----------------\n");
56
57         unistr2_to_ascii(temp, &a->uni_acct_name, sizeof(temp)-1);
58         printf("\tUser name          :%s\n", temp);
59         printf("\tuser's rid         :%d\n", a->user_rid);
60         printf("\tuser's primary gid :%d\n", a->group_rid);
61         unistr2_to_ascii(temp, &a->uni_full_name, sizeof(temp)-1);
62         printf("\tfull name          :%s\n", temp);
63         unistr2_to_ascii(temp, &a->uni_home_dir, sizeof(temp)-1);
64         printf("\thome directory     :%s\n", temp);
65         unistr2_to_ascii(temp, &a->uni_dir_drive, sizeof(temp)-1);
66         printf("\tdrive              :%s\n", temp);
67         unistr2_to_ascii(temp, &a->uni_logon_script, sizeof(temp)-1);
68         printf("\tlogon script       :%s\n", temp);
69         unistr2_to_ascii(temp, &a->uni_acct_desc, sizeof(temp)-1);
70         printf("\tdescription        :%s\n", temp);
71         unistr2_to_ascii(temp, &a->uni_workstations, sizeof(temp)-1);
72         printf("\tworkstations       :%s\n", temp);
73 }
74
75 static void decode_sam_grp_mem_info(SAM_GROUP_MEM_INFO *a)
76 {
77         int i;
78         printf("\nGroup members information\n");
79         printf("-------------------------\n");
80         printf("\tnum members        :%d\n", a->num_members);
81
82         for (i=0; i<a->num_members; i++) {
83                 printf("\trid, attr:%d, %d\n", a->rids[i], a->attribs[i]);
84         }
85 }
86
87 static void decode_sam_alias_info(SAM_ALIAS_INFO *a)
88 {
89         fstring temp;
90         printf("\nAlias Information\n");
91         printf("-----------------\n");
92
93         unistr2_to_ascii(temp, &a->uni_als_name, sizeof(temp)-1);
94         printf("\tname               :%s\n", temp);
95         unistr2_to_ascii(temp, &a->uni_als_desc, sizeof(temp)-1);
96         printf("\tdescription        :%s\n", temp);
97         printf("\trid                :%d\n", a->als_rid);
98 }
99
100 static void decode_sam_als_mem_info(SAM_ALIAS_MEM_INFO *a)
101 {
102         int i;
103         fstring temp;
104         printf("\nAlias members Information\n");
105         printf("-------------------------\n");
106         printf("\tnum members        :%d\n", a->num_members);
107         printf("\tnum sids           :%d\n", a->num_sids);
108         for (i=0; i<a->num_sids; i++) {
109                 printf("\tsid                :%s\n", sid_to_string(temp, &a->sids[i].sid));
110         }
111
112
113 }
114
115 static void decode_sam_dom_info(SAM_DELTA_DOM *a)
116 {
117         fstring temp;
118         printf("\nDomain information\n");
119         printf("------------------\n");
120
121         unistr2_to_ascii(temp, &a->domain_name, sizeof(temp)-1);
122         printf("\tdomain name        :%s\n", temp);
123         printf("\tsid                :%s\n", sid_to_string(temp, &a->domain_sid.sid));
124 }
125
126 static void decode_sam_unk0e_info(SAM_DELTA_UNK0E *a)
127 {
128         fstring temp;
129         printf("\nTrust information\n");
130         printf("-----------------\n");
131
132         unistr2_to_ascii(temp, &a->domain, sizeof(temp)-1);
133         printf("\tdomain name        :%s\n", temp);
134         printf("\tsid                :%s\n", sid_to_string(temp, &a->sid.sid));
135         display_sec_desc(a->sec_desc);
136 }
137
138 static void decode_sam_privs_info(SAM_DELTA_PRIVS *a)
139 {
140         int i;
141         fstring temp;
142         printf("\nSID and privileges information\n");
143         printf("------------------------------\n");
144         printf("\tsid                :%s\n", sid_to_string(temp, &a->sid.sid));
145         display_sec_desc(a->sec_desc);
146         printf("\tprivileges count   :%d\n", a->privlist_count);
147         for (i=0; i<a->privlist_count; i++) {
148                 unistr2_to_ascii(temp, &a->uni_privslist[i], sizeof(temp)-1);
149                 printf("\tprivilege name     :%s\n", temp);
150                 printf("\tattribute          :%d\n", a->attributes[i]);
151         }
152 }
153
154 static void decode_sam_unk12_info(SAM_DELTA_UNK12 *a)
155 {
156         fstring temp;
157         printf("\nTrusted information\n");
158         printf("-------------------\n");
159
160         unistr2_to_ascii(temp, &a->secret, sizeof(temp)-1);
161         printf("\tsecret name        :%s\n", temp);
162         display_sec_desc(a->sec_desc);
163         
164         printf("\ttime 1             :%s\n", http_timestring(nt_time_to_unix(&a->time1)));
165         printf("\ttime 2             :%s\n", http_timestring(nt_time_to_unix(&a->time2)));
166
167         display_sec_desc(a->sec_desc2);
168 }
169
170 static void decode_sam_stamp(SAM_DELTA_STAMP *a)
171 {
172         printf("\nStamp information\n");
173         printf("-----------------\n");
174         printf("\tsequence number    :%d\n", a->seqnum);
175 }
176
177 static void decode_sam_deltas(uint32 num_deltas, SAM_DELTA_HDR *hdr_deltas, SAM_DELTA_CTR *deltas)
178 {
179         int i;
180         for (i = 0; i < num_deltas; i++) {
181                 switch (hdr_deltas[i].type) {
182                         case SAM_DELTA_DOMAIN_INFO: {
183                                 SAM_DOMAIN_INFO *a;
184                                 a = &deltas[i].domain_info;
185                                 decode_domain_info(a);
186                                 break;
187                         }
188                         case SAM_DELTA_GROUP_INFO:  {
189                                 SAM_GROUP_INFO *a;
190                                 a = &deltas[i].group_info;
191                                 decode_sam_group_info(a);
192                                 break;
193                         }
194                         case SAM_DELTA_ACCOUNT_INFO: {
195                                 SAM_ACCOUNT_INFO *a;
196                                 a = &deltas[i].account_info;
197                                 decode_sam_account_info(a);
198                                 break;
199                         }
200                         case SAM_DELTA_GROUP_MEM: {
201                                 SAM_GROUP_MEM_INFO *a;
202                                 a = &deltas[i].grp_mem_info;
203                                 decode_sam_grp_mem_info(a);
204                                 break;
205                         }
206                         case SAM_DELTA_ALIAS_INFO: {
207                                 SAM_ALIAS_INFO *a;
208                                 a = &deltas[i].alias_info;
209                                 decode_sam_alias_info(a);
210                                 break;
211                         }
212                         case SAM_DELTA_ALIAS_MEM: {
213                                 SAM_ALIAS_MEM_INFO *a;
214                                 a = &deltas[i].als_mem_info;
215                                 decode_sam_als_mem_info(a);
216                                 break;
217                         }
218                         case SAM_DELTA_DOM_INFO: {
219                                 SAM_DELTA_DOM *a;
220                                 a = &deltas[i].dom_info;
221                                 decode_sam_dom_info(a);
222                                 break;
223                         }
224                         case SAM_DELTA_UNK0E_INFO: {
225                                 SAM_DELTA_UNK0E *a;
226                                 a = &deltas[i].unk0e_info;
227                                 decode_sam_unk0e_info(a);
228                                 break;
229                         }
230                         case SAM_DELTA_PRIVS_INFO: {
231                                 SAM_DELTA_PRIVS *a;
232                                 a = &deltas[i].privs_info;
233                                 decode_sam_privs_info(a);
234                                 break;
235                         }
236                         case SAM_DELTA_UNK12_INFO: {
237                                 SAM_DELTA_UNK12 *a;
238                                 a = &deltas[i].unk12_info;
239                                 decode_sam_unk12_info(a);
240                                 break;
241                         }
242                         case SAM_DELTA_SAM_STAMP: {
243                                 SAM_DELTA_STAMP *a;
244                                 a = &deltas[i].stamp;
245                                 decode_sam_stamp(a);
246                                 break;
247                         }
248                         default:
249                                 DEBUG(0,("unknown delta type: %d\n", hdr_deltas[i].type));
250                                 break;  
251                 }
252         }
253 }
254
255 /* Synchronise sam database */
256
257 static NTSTATUS sam_sync(struct cli_state *cli, unsigned char trust_passwd[16],
258                          BOOL do_smbpasswd_output, BOOL verbose)
259 {
260         TALLOC_CTX *mem_ctx;
261         SAM_DELTA_HDR *hdr_deltas_0, *hdr_deltas_1, *hdr_deltas_2;
262         SAM_DELTA_CTR *deltas_0, *deltas_1, *deltas_2;
263         uint32 num_deltas_0, num_deltas_1, num_deltas_2;
264         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
265
266         DOM_CRED ret_creds;
267         /* Initialise */
268
269         if (!(mem_ctx = talloc_init())) {
270                 DEBUG(0,("talloc_init failed\n"));
271                 return result;
272         }
273
274         if (!cli_nt_session_open (cli, PIPE_NETLOGON)) {
275                 DEBUG(0, ("Could not initialize netlogon pipe!\n"));
276                 goto done;
277         }
278
279         /* Request a challenge */
280
281         if (!NT_STATUS_IS_OK(new_cli_nt_setup_creds(cli, SEC_CHAN_BDC, trust_passwd))) {
282                 DEBUG(0, ("Error initialising session creds\n"));
283                 goto done;
284         }
285
286         /* on first call the returnAuthenticator is empty */
287         memset(&ret_creds, 0, sizeof(ret_creds));
288
289         /* Do sam synchronisation on the SAM database*/
290
291         result = cli_netlogon_sam_sync(cli, mem_ctx, &ret_creds, 0, &num_deltas_0, &hdr_deltas_0, &deltas_0);
292         
293         if (!NT_STATUS_IS_OK(result))
294                 goto done;
295
296         /* verbose mode */
297         if (verbose)
298                 decode_sam_deltas(num_deltas_0, hdr_deltas_0, deltas_0);
299
300
301         /* 
302          * we can't yet do several sam_sync in a raw, it's a credential problem 
303          * we must chain the credentials
304          */
305
306 #if 1
307         /* Do sam synchronisation on the LSA database */
308
309         result = cli_netlogon_sam_sync(cli, mem_ctx, &ret_creds, 2, &num_deltas_2, &hdr_deltas_2, &deltas_2);
310         
311         if (!NT_STATUS_IS_OK(result))
312                 goto done;
313
314         /* verbose mode */
315         if (verbose)
316                 decode_sam_deltas(num_deltas_2, hdr_deltas_2, deltas_2);
317 #endif
318
319         /* Produce smbpasswd output - good for migrating from NT! */
320
321         if (do_smbpasswd_output) {
322                 int i;
323
324                 for (i = 0; i < num_deltas_0; i++) {
325                         SAM_ACCOUNT_INFO *a;
326                         fstring acct_name, hex_nt_passwd, hex_lm_passwd;
327                         uchar lm_passwd[16], nt_passwd[16];
328
329                         /* Skip non-user accounts */
330
331                         if (hdr_deltas_0[i].type != SAM_DELTA_ACCOUNT_INFO)
332                                 continue;
333
334                         a = &deltas_0[i].account_info;
335
336                         unistr2_to_ascii(acct_name, &a->uni_acct_name,
337                                          sizeof(acct_name) - 1);
338
339                         /* Decode hashes from password hash */
340
341                         sam_pwd_hash(a->user_rid, a->pass.buf_lm_pwd, 
342                                      lm_passwd, 0);
343                         sam_pwd_hash(a->user_rid, a->pass.buf_nt_pwd, 
344                                      nt_passwd, 0);
345
346                         /* Encode as strings */
347
348                         smbpasswd_sethexpwd(hex_lm_passwd, lm_passwd,
349                                             a->acb_info);
350                         smbpasswd_sethexpwd(hex_nt_passwd, nt_passwd,
351                                             a->acb_info);
352
353                         /* Display user info */
354
355                         printf("%s:%d:%s:%s:%s:LCT-0\n", acct_name,
356                                a->user_rid, hex_lm_passwd, hex_nt_passwd,
357                                smbpasswd_encode_acb_info(a->acb_info));
358                 }
359                                
360                 goto done;
361         }
362
363         /* Update sam tdb */
364
365  done:
366         cli_nt_session_close(cli);
367         talloc_destroy(mem_ctx);
368         
369         return result;
370 }
371
372 /* Replicate sam deltas */
373
374 static NTSTATUS sam_repl(struct cli_state *cli, unsigned char trust_passwde[16],
375                          uint32 low_serial)
376 {
377         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
378
379         return result;
380 }
381
382 /* Print usage information */
383
384 static void usage(void)
385 {
386         printf("Usage: samsync [options]\n");
387
388         printf("\t-d debuglevel         set the debuglevel\n");
389         printf("\t-h                    Print this help message.\n");
390         printf("\t-s configfile         specify an alternative config file\n");
391         printf("\t-S                    synchronise sam database\n");
392         printf("\t-R                    replicate sam deltas\n");
393         printf("\t-U username           username and password\n");
394         printf("\t-p                    produce smbpasswd output\n");
395         printf("\t-V                    verbose output\n");
396         printf("\n");
397 }
398
399 /* Connect to primary domain controller */
400
401 static struct cli_state *init_connection(struct cli_state **cli,
402                                          char *username, char *domain,
403                                          char *password)
404 {
405         extern pstring global_myname;
406         struct in_addr *dest_ip;
407         int count;
408         fstring dest_host;
409
410         /* Initialise cli_state information */
411
412         /* Look up name of PDC controller */
413
414         if (!get_dc_list(True, lp_workgroup(), &dest_ip, &count)) {
415                 DEBUG(0, ("Cannot find domain controller for domain %s\n",
416                           lp_workgroup()));
417                 return NULL;
418         }
419
420         if (!lookup_dc_name(global_myname, lp_workgroup(), dest_ip, 
421                             dest_host)) {
422                 DEBUG(0, ("Could not lookup up PDC name for domain %s\n",
423                           lp_workgroup()));
424                 return NULL;
425         }
426
427         if (NT_STATUS_IS_OK(cli_full_connection(cli, global_myname, dest_host,
428                                                 dest_ip, 0,
429                                                 "IPC$", "IPC",  
430                                                 username, domain,
431                                                 password, strlen(password)))) {
432                 return *cli;
433         } else {
434                 return NULL;
435         }
436 }
437
438 /* Main function */
439
440  int main(int argc, char **argv)
441 {
442         BOOL do_sam_sync = False, do_sam_repl = False;
443         struct cli_state *cli;
444         NTSTATUS result;
445         int opt;
446         pstring logfile;
447         BOOL interactive = False, do_smbpasswd_output = False;
448         BOOL verbose = False;
449         uint32 low_serial = 0;
450         unsigned char trust_passwd[16];
451         fstring username, domain, password;
452
453         if (argc == 1) {
454                 usage();
455                 return 1;
456         }
457
458         ZERO_STRUCT(username);
459         ZERO_STRUCT(domain);
460         ZERO_STRUCT(password);
461
462         /* Parse command line options */
463
464         while((opt = getopt(argc, argv, "s:d:SR:hiU:W:pV")) != EOF) {
465                 switch (opt) {
466                 case 's':
467                         pstrcpy(dyn_CONFIGFILE, optarg);
468                         break;
469                 case 'd':
470                         DEBUGLEVEL = atoi(optarg);
471                         break;
472                 case 'S':
473                         do_sam_sync = 1;
474                         break;
475                 case 'R':
476                         do_sam_repl = 1;
477                         low_serial = atoi(optarg);
478                         break;
479                 case 'i':
480                         interactive = True;
481                         break;
482                 case 'U': {
483                         char *lp;
484
485                         fstrcpy(username,optarg);
486                         if ((lp=strchr_m(username,'%'))) {
487                                 *lp = 0;
488                                 fstrcpy(password,lp+1);
489                                 memset(strchr_m(optarg, '%') + 1, 'X',
490                                        strlen(password));
491                         }
492                         break;
493                 }
494                 case 'W':
495                         pstrcpy(domain, optarg);
496                         break;
497                 case 'p':
498                         do_smbpasswd_output = True;
499                         break;
500                 case 'V':
501                         verbose = True;
502                         break;
503                case 'h':
504                 default:
505                         usage();
506                         exit(1);
507                 }
508         }
509
510         argc -= optind;
511
512         if (argc > 0) {
513                 usage();
514                 return 1;
515         }
516
517         /* Initialise samba */
518
519         slprintf(logfile, sizeof(logfile) - 1, "%s/log.%s", dyn_LOGFILEBASE, 
520                  "samsync");
521         lp_set_logfile(logfile);
522
523         setup_logging("samsync", interactive);
524
525         if (!interactive)
526                 reopen_logs();
527
528         if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
529                 fprintf(stderr, "Can't load %s\n", dyn_CONFIGFILE);
530         }
531
532         load_interfaces();
533
534         /* Check arguments make sense */
535
536         if (do_sam_sync && do_sam_repl) {
537                 fprintf(stderr, "cannot specify both -S and -R\n");
538                 return 1;
539
540         }
541
542         if (!do_sam_sync && !do_sam_repl) {
543                 fprintf(stderr, "must specify either -S or -R\n");
544                 return 1;
545         }
546
547         if (do_sam_repl && low_serial == 0) {
548                 fprintf(stderr, "serial number must be positive\n");
549                 return 1;
550         }
551
552         /* BDC operations require the machine account password */
553
554         if (!secrets_init()) {
555                 DEBUG(0, ("Unable to initialise secrets database\n"));
556                 return 1;
557         }
558
559         if (!secrets_fetch_trust_account_password(lp_workgroup(), 
560                                                   trust_passwd, NULL)) {
561                 DEBUG(0, ("could not fetch trust account password\n"));
562                 return 1;
563         }        
564
565         /* Perform sync or replication */
566
567         if (!init_connection(&cli, username, domain, password))
568                 return 1;
569
570         if (do_sam_sync)
571                 result = sam_sync(cli, trust_passwd, do_smbpasswd_output, verbose);
572
573         if (do_sam_repl)
574                 result = sam_repl(cli, trust_passwd, low_serial);
575
576         if (!NT_STATUS_IS_OK(result)) {
577                 DEBUG(0, ("%s\n", nt_errstr(result)));
578                 return 1;
579         }
580
581         return 0;
582 }