2 Unix SMB/CIFS implementation.
3 SAM synchronisation and replication
5 Copyright (C) Tim Potter 2001,2002
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.
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.
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.
26 static void decode_domain_info(SAM_DOMAIN_INFO *a)
29 printf("Domain Information\n");
30 printf("------------------\n");
32 unistr2_to_ascii(temp, &a->uni_dom_name, sizeof(temp)-1);
33 printf("\tDomain :%s\n", temp);
34 printf("\tMin password len :%d\n", a->min_pwd_len);
35 printf("\tpassword history len:%d\n", a->pwd_history_len);
36 printf("\tcreation time :%s\n", http_timestring(nt_time_to_unix(&a->creation_time)));
39 static void decode_sam_group_info(SAM_GROUP_INFO *a)
42 printf("\nDomain Group Information\n");
43 printf("------------------------\n");
45 unistr2_to_ascii(temp, &a->uni_grp_name, sizeof(temp)-1);
46 printf("\tGroup name :%s\n", temp);
47 unistr2_to_ascii(temp, &a->uni_grp_desc, sizeof(temp)-1);
48 printf("\tGroup description :%s\n", temp);
49 printf("\trid :%d\n", a->gid.g_rid);
50 printf("\tattribute :%d\n", a->gid.attr);
53 static void decode_sam_account_info(SAM_ACCOUNT_INFO *a)
56 printf("\nUser Information\n");
57 printf("----------------\n");
59 unistr2_to_ascii(temp, &a->uni_acct_name, sizeof(temp)-1);
60 printf("\tUser name :%s\n", temp);
61 printf("\tuser's rid :%d\n", a->user_rid);
62 printf("\tuser's primary gid :%d\n", a->group_rid);
63 unistr2_to_ascii(temp, &a->uni_full_name, sizeof(temp)-1);
64 printf("\tfull name :%s\n", temp);
65 unistr2_to_ascii(temp, &a->uni_home_dir, sizeof(temp)-1);
66 printf("\thome directory :%s\n", temp);
67 unistr2_to_ascii(temp, &a->uni_dir_drive, sizeof(temp)-1);
68 printf("\tdrive :%s\n", temp);
69 unistr2_to_ascii(temp, &a->uni_logon_script, sizeof(temp)-1);
70 printf("\tlogon script :%s\n", temp);
71 unistr2_to_ascii(temp, &a->uni_acct_desc, sizeof(temp)-1);
72 printf("\tdescription :%s\n", temp);
73 unistr2_to_ascii(temp, &a->uni_workstations, sizeof(temp)-1);
74 printf("\tworkstations :%s\n", temp);
77 static void decode_sam_grp_mem_info(SAM_GROUP_MEM_INFO *a)
80 printf("\nGroup members information\n");
81 printf("-------------------------\n");
82 printf("\tnum members :%d\n", a->num_members);
84 for (i=0; i<a->num_members; i++) {
85 printf("\trid, attr:%d, %d\n", a->rids[i], a->attribs[i]);
89 static void decode_sam_alias_info(SAM_ALIAS_INFO *a)
92 printf("\nAlias Information\n");
93 printf("-----------------\n");
95 unistr2_to_ascii(temp, &a->uni_als_name, sizeof(temp)-1);
96 printf("\tname :%s\n", temp);
97 unistr2_to_ascii(temp, &a->uni_als_desc, sizeof(temp)-1);
98 printf("\tdescription :%s\n", temp);
99 printf("\trid :%d\n", a->als_rid);
102 static void decode_sam_als_mem_info(SAM_ALIAS_MEM_INFO *a)
106 printf("\nAlias members Information\n");
107 printf("-------------------------\n");
108 printf("\tnum members :%d\n", a->num_members);
109 printf("\tnum sids :%d\n", a->num_sids);
110 for (i=0; i<a->num_sids; i++) {
111 printf("\tsid :%s\n", sid_to_string(temp, &a->sids[i].sid));
117 static void decode_sam_dom_info(SAM_DELTA_DOM *a)
120 printf("\nDomain information\n");
121 printf("------------------\n");
123 unistr2_to_ascii(temp, &a->domain_name, sizeof(temp)-1);
124 printf("\tdomain name :%s\n", temp);
125 printf("\tsid :%s\n", sid_to_string(temp, &a->domain_sid.sid));
128 static void decode_sam_unk0e_info(SAM_DELTA_UNK0E *a)
131 printf("\nTrust information\n");
132 printf("-----------------\n");
134 unistr2_to_ascii(temp, &a->domain, sizeof(temp)-1);
135 printf("\tdomain name :%s\n", temp);
136 printf("\tsid :%s\n", sid_to_string(temp, &a->sid.sid));
137 display_sec_desc(a->sec_desc);
140 static void decode_sam_privs_info(SAM_DELTA_PRIVS *a)
144 printf("\nSID and privileges information\n");
145 printf("------------------------------\n");
146 printf("\tsid :%s\n", sid_to_string(temp, &a->sid.sid));
147 display_sec_desc(a->sec_desc);
148 printf("\tprivileges count :%d\n", a->privlist_count);
149 for (i=0; i<a->privlist_count; i++) {
150 unistr2_to_ascii(temp, &a->uni_privslist[i], sizeof(temp)-1);
151 printf("\tprivilege name :%s\n", temp);
152 printf("\tattribute :%d\n", a->attributes[i]);
156 static void decode_sam_unk12_info(SAM_DELTA_UNK12 *a)
159 printf("\nTrusted information\n");
160 printf("-------------------\n");
162 unistr2_to_ascii(temp, &a->secret, sizeof(temp)-1);
163 printf("\tsecret name :%s\n", temp);
164 display_sec_desc(a->sec_desc);
166 printf("\ttime 1 :%s\n", http_timestring(nt_time_to_unix(&a->time1)));
167 printf("\ttime 2 :%s\n", http_timestring(nt_time_to_unix(&a->time2)));
169 display_sec_desc(a->sec_desc2);
172 static void decode_sam_stamp(SAM_DELTA_STAMP *a)
174 printf("\nStamp information\n");
175 printf("-----------------\n");
176 printf("\tsequence number :%d\n", a->seqnum);
179 static void decode_sam_deltas(uint32 num_deltas, SAM_DELTA_HDR *hdr_deltas, SAM_DELTA_CTR *deltas)
182 for (i = 0; i < num_deltas; i++) {
183 switch (hdr_deltas[i].type) {
184 case SAM_DELTA_DOMAIN_INFO: {
186 a = &deltas[i].domain_info;
187 decode_domain_info(a);
190 case SAM_DELTA_GROUP_INFO: {
192 a = &deltas[i].group_info;
193 decode_sam_group_info(a);
196 case SAM_DELTA_ACCOUNT_INFO: {
198 a = &deltas[i].account_info;
199 decode_sam_account_info(a);
202 case SAM_DELTA_GROUP_MEM: {
203 SAM_GROUP_MEM_INFO *a;
204 a = &deltas[i].grp_mem_info;
205 decode_sam_grp_mem_info(a);
208 case SAM_DELTA_ALIAS_INFO: {
210 a = &deltas[i].alias_info;
211 decode_sam_alias_info(a);
214 case SAM_DELTA_ALIAS_MEM: {
215 SAM_ALIAS_MEM_INFO *a;
216 a = &deltas[i].als_mem_info;
217 decode_sam_als_mem_info(a);
220 case SAM_DELTA_DOM_INFO: {
222 a = &deltas[i].dom_info;
223 decode_sam_dom_info(a);
226 case SAM_DELTA_UNK0E_INFO: {
228 a = &deltas[i].unk0e_info;
229 decode_sam_unk0e_info(a);
232 case SAM_DELTA_PRIVS_INFO: {
234 a = &deltas[i].privs_info;
235 decode_sam_privs_info(a);
238 case SAM_DELTA_UNK12_INFO: {
240 a = &deltas[i].unk12_info;
241 decode_sam_unk12_info(a);
244 case SAM_DELTA_SAM_STAMP: {
246 a = &deltas[i].stamp;
251 DEBUG(0,("unknown delta type: %d\n", hdr_deltas[i].type));
257 /* Convert a SAM_ACCOUNT_DELTA to a SAM_ACCOUNT. */
259 static void sam_account_from_delta(SAM_ACCOUNT *account,
260 SAM_ACCOUNT_INFO *delta)
265 /* Username, fullname, home dir, dir drive, logon script, acct
266 desc, workstations, profile. */
268 unistr2_to_ascii(s, &delta->uni_acct_name, sizeof(s) - 1);
269 pdb_set_nt_username(account, s);
271 /* Unix username is the same - for sainity */
272 pdb_set_username(account, s);
274 unistr2_to_ascii(s, &delta->uni_full_name, sizeof(s) - 1);
275 pdb_set_fullname(account, s);
277 unistr2_to_ascii(s, &delta->uni_home_dir, sizeof(s) - 1);
278 pdb_set_homedir(account, s, True);
280 unistr2_to_ascii(s, &delta->uni_dir_drive, sizeof(s) - 1);
281 pdb_set_dir_drive(account, s, True);
283 unistr2_to_ascii(s, &delta->uni_logon_script, sizeof(s) - 1);
284 pdb_set_logon_script(account, s, True);
286 unistr2_to_ascii(s, &delta->uni_acct_desc, sizeof(s) - 1);
287 pdb_set_acct_desc(account, s);
289 unistr2_to_ascii(s, &delta->uni_workstations, sizeof(s) - 1);
290 pdb_set_workstations(account, s);
292 unistr2_to_ascii(s, &delta->uni_profile, sizeof(s) - 1);
293 pdb_set_profile_path(account, s, True);
295 /* User and group sid */
297 sid_copy(&sid, &domain_sid);
298 sid_append_rid(&sid, delta->user_rid);
299 pdb_set_user_sid(account, &sid);
301 sid_copy(&sid, &domain_sid);
302 sid_append_rid(&sid, delta->group_rid);
303 pdb_set_group_sid(account, &sid);
305 /* Logon and password information */
307 pdb_set_logon_time(account, nt_time_to_unix(&delta->logon_time), True);
308 pdb_set_logoff_time(account, nt_time_to_unix(&delta->logoff_time),
311 pdb_set_logon_divs(account, delta->logon_divs);
313 /* TODO: logon hours */
314 /* TODO: bad password count */
315 /* TODO: logon count */
317 pdb_set_pass_last_set_time(
318 account, nt_time_to_unix(&delta->pwd_last_set_time));
320 /* TODO: account expiry time */
322 pdb_set_acct_ctrl(account, delta->acb_info);
325 static void apply_account_info(SAM_ACCOUNT_INFO *sam_acct_delta)
327 SAM_ACCOUNT sam_acct;
330 ZERO_STRUCT(sam_acct);
332 pdb_init_sam(&sam_acct);
334 sam_account_from_delta(&sam_acct, sam_acct_delta);
335 result = pdb_add_sam_account(&sam_acct);
338 /* Apply an array of deltas to the SAM database */
340 static void apply_deltas(uint32 num_deltas, SAM_DELTA_HDR *hdr_deltas,
341 SAM_DELTA_CTR *deltas)
345 for (i = 0; i < num_deltas; i++) {
346 switch(hdr_deltas[i].type) {
347 case SAM_DELTA_ACCOUNT_INFO:
348 apply_account_info(&deltas[i].account_info);
354 /* Synchronise sam database */
356 static NTSTATUS sam_sync(struct cli_state *cli, unsigned char trust_passwd[16],
357 BOOL do_smbpasswd_output, BOOL verbose)
360 SAM_DELTA_HDR *hdr_deltas_0, *hdr_deltas_2;
361 SAM_DELTA_CTR *deltas_0, *deltas_2;
362 uint32 num_deltas_0, num_deltas_2;
363 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
364 struct pdb_context *in;
370 if (!NT_STATUS_IS_OK(make_pdb_context_list(&in, lp_passdb_backend()))){
371 DEBUG(0, ("Can't initialize passdb backend.\n"));
375 if (!(mem_ctx = talloc_init())) {
376 DEBUG(0,("talloc_init failed\n"));
380 if (!cli_nt_session_open (cli, PIPE_NETLOGON)) {
381 DEBUG(0, ("Could not initialize netlogon pipe!\n"));
385 /* Request a challenge */
387 if (!NT_STATUS_IS_OK(cli_nt_setup_creds(cli, SEC_CHAN_BDC, trust_passwd))) {
388 DEBUG(0, ("Error initialising session creds\n"));
392 /* on first call the returnAuthenticator is empty */
393 memset(&ret_creds, 0, sizeof(ret_creds));
395 /* Do sam synchronisation on the SAM database*/
397 result = cli_netlogon_sam_sync(cli, mem_ctx, &ret_creds, 0,
398 &num_deltas_0, &hdr_deltas_0,
401 if (!NT_STATUS_IS_OK(result))
404 apply_deltas(num_deltas_0, hdr_deltas_0, deltas_0);
408 * we can't yet do several sam_sync in a raw, it's a credential problem
409 * we must chain the credentials
413 /* Do sam synchronisation on the LSA database */
415 result = cli_netlogon_sam_sync(cli, mem_ctx, &ret_creds, 2, &num_deltas_2, &hdr_deltas_2, &deltas_2);
417 if (!NT_STATUS_IS_OK(result))
422 decode_sam_deltas(num_deltas_2, hdr_deltas_2, deltas_2);
425 /* Produce smbpasswd output - good for migrating from NT! */
427 if (do_smbpasswd_output) {
430 for (i = 0; i < num_deltas_0; i++) {
432 fstring acct_name, hex_nt_passwd, hex_lm_passwd;
433 uchar lm_passwd[16], nt_passwd[16];
435 /* Skip non-user accounts */
437 if (hdr_deltas_0[i].type != SAM_DELTA_ACCOUNT_INFO)
440 a = &deltas_0[i].account_info;
442 unistr2_to_ascii(acct_name, &a->uni_acct_name,
443 sizeof(acct_name) - 1);
445 /* Decode hashes from password hash */
447 sam_pwd_hash(a->user_rid, a->pass.buf_lm_pwd,
449 sam_pwd_hash(a->user_rid, a->pass.buf_nt_pwd,
452 /* Encode as strings */
454 smbpasswd_sethexpwd(hex_lm_passwd, lm_passwd,
456 smbpasswd_sethexpwd(hex_nt_passwd, nt_passwd,
459 /* Display user info */
461 printf("%s:%d:%s:%s:%s:LCT-0\n", acct_name,
462 a->user_rid, hex_lm_passwd, hex_nt_passwd,
463 smbpasswd_encode_acb_info(a->acb_info));
472 cli_nt_session_close(cli);
473 talloc_destroy(mem_ctx);
478 /* Replicate sam deltas */
480 static NTSTATUS sam_repl(struct cli_state *cli, unsigned char trust_passwde[16],
483 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
488 /* Connect to primary domain controller */
490 static struct cli_state *init_connection(struct cli_state **cli,
491 char *username, char *domain,
494 extern pstring global_myname;
495 struct in_addr *dest_ip;
499 /* Initialise myname */
501 if (!global_myname[0]) {
504 fstrcpy(global_myname, myhostname());
505 p = strchr(global_myname, '.');
510 /* Look up name of PDC controller */
512 if (!get_dc_list(True, lp_workgroup(), &dest_ip, &count)) {
513 DEBUG(0, ("Cannot find domain controller for domain %s\n",
518 if (!lookup_dc_name(global_myname, lp_workgroup(), dest_ip,
520 DEBUG(0, ("Could not lookup up PDC name for domain %s\n",
525 if (NT_STATUS_IS_OK(cli_full_connection(cli, global_myname, dest_host,
538 static fstring popt_username, popt_domain, popt_password;
539 static BOOL popt_got_pass;
541 static void user_callback(poptContext con,
542 enum poptCallbackReason reason,
543 const struct poptOption *opt,
544 const char *arg, const void *data)
553 /* Check for [DOMAIN\\]username[%password]*/
559 if ((ch = strchr(p, '\\'))) {
560 fstrcpy(popt_domain, p);
561 popt_domain[ch - p] = 0;
564 fstrcpy(popt_username, p);
566 if ((ch = strchr(p, '%'))) {
567 popt_username[ch - p] = 0;
568 fstrcpy(popt_password, ch + 1);
569 popt_got_pass = True;
575 fstrcpy(popt_domain, arg);
580 /* Return domain, username and password passed in from cmd line */
582 void popt_common_get_auth_info(char **domain, char **username, char **password,
585 *domain = popt_domain;
586 *username = popt_username;
587 *password = popt_password;
588 *got_pass = popt_got_pass;
591 struct poptOption popt_common_auth_info[] = {
592 { NULL, 0, POPT_ARG_CALLBACK, user_callback },
593 { "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set username",
594 "[DOMAIN\\]username[%password]" },
595 { "domain", 'W', POPT_ARG_STRING, NULL, 'W', "Set domain name",
600 static BOOL popt_interactive;
602 BOOL popt_common_is_interactive(void)
604 return popt_interactive;
607 struct poptOption popt_common_interactive[] = {
608 { "interactive", 'i', POPT_ARG_NONE, &popt_interactive, 'i',
613 int main(int argc, char **argv)
615 BOOL do_sam_sync = False, do_sam_repl = False;
616 struct cli_state *cli;
619 BOOL do_smbpasswd_output = False;
620 BOOL verbose = True, got_pass = False;
622 unsigned char trust_passwd[16];
623 char *username, *domain, *password;
627 struct poptOption popt_samsync_opts[] = {
628 { "synchronise", 'S', POPT_ARG_NONE, &do_sam_sync, 'S',
629 "Perform full SAM synchronisation" },
630 { "replicate", 'R', POPT_ARG_NONE, &do_sam_repl, 'R',
631 "Replicate SAM changes" },
632 { "serial", 0, POPT_ARG_INT, &serial, 0, "SAM serial number" },
633 { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_debug },
634 { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_auth_info },
635 { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_interactive },
640 /* Read command line options */
642 pc = poptGetContext("samsync", argc, (const char **)argv,
643 popt_samsync_opts, 0);
646 poptPrintUsage(pc, stdout, 0);
650 while ((c = poptGetNextOpt(pc)) != -1) {
652 /* Argument processing error */
655 fprintf(stderr, "samsync: %s: %s\n",
656 poptBadOption(pc, POPT_BADOPTION_NOALIAS),
661 /* Handle arguments */
665 poptPrintHelp(pc, stdout, 0);
668 poptPrintUsage(pc, stdout, 0);
673 /* Bail out if any extra args were passed */
675 if (poptPeekArg(pc)) {
676 fprintf(stderr, "samsync: invalid argument %s\n",
678 poptPrintUsage(pc, stdout, 0);
688 if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
689 d_fprintf(stderr, "samsync: error opening config file %s. "
690 "Error was %s\n", dyn_CONFIGFILE, strerror(errno));
694 slprintf(logfile, sizeof(logfile) - 1, "%s/log.%s", dyn_LOGFILEBASE,
697 lp_set_logfile(logfile);
699 setup_logging("samsync", popt_common_is_interactive());
701 if (!popt_common_is_interactive())
706 /* Check arguments make sense */
708 if (do_sam_sync && do_sam_repl) {
709 DEBUG(0, ("cannot specify both -S and -R\n"));
714 if (!do_sam_sync && !do_sam_repl) {
715 DEBUG(0, ("samsync: you must either --synchronise or "
716 "--replicate the SAM database\n"));
720 if (do_sam_repl && serial == 0) {
721 DEBUG(0, ("samsync: must specify serial number\n"));
725 if (do_sam_sync && serial != 0) {
726 DEBUG(0, ("samsync: you can't specify a serial number when "
727 "synchonising the SAM database\n"));
731 /* BDC operations require the machine account password */
733 if (!secrets_init()) {
734 DEBUG(0, ("samsync: unable to initialise secrets database\n"));
738 if (!secrets_fetch_trust_account_password(lp_workgroup(),
739 trust_passwd, NULL)) {
740 DEBUG(0, ("samsync: could not fetch trust account password\n"));
744 /* I wish the domain sid wasn't stored in secrets.tdb */
746 if (!secrets_fetch_domain_sid(lp_workgroup(), &domain_sid)) {
747 DEBUG(0, ("samsync: could not retrieve domain sid\n"));
751 /* Perform sync or replication */
753 popt_common_get_auth_info(&domain, &username, &password, &got_pass);
755 if (!init_connection(&cli, username, domain, password))
759 result = sam_sync(cli, trust_passwd, do_smbpasswd_output,
763 result = sam_repl(cli, trust_passwd, serial);
765 if (!NT_STATUS_IS_OK(result)) {
766 DEBUG(0, ("%s\n", nt_errstr(result)));