From 3a99ab6aa546b3cf67fdece29e46dec5c9817271 Mon Sep 17 00:00:00 2001 From: Tim Potter Date: Mon, 29 Jul 2002 09:32:25 +0000 Subject: Started to get samsync to insert account information in the passdb. It's pretty half-arsed at the moment and doesn't work very well but Mr Bartlett was interested in it. Also started playing around with the more interesting bits of popt. The auto-generated usage information is pretty neat. (This used to be commit b3e51bfe6c13f1d20e599f675332f0489d8462e7) --- source3/rpcclient/samsync.c | 419 ++++++++++++++++++++++++++++++++------------ 1 file changed, 309 insertions(+), 110 deletions(-) diff --git a/source3/rpcclient/samsync.c b/source3/rpcclient/samsync.c index 802666841d..e20322cbc5 100644 --- a/source3/rpcclient/samsync.c +++ b/source3/rpcclient/samsync.c @@ -1,8 +1,8 @@ /* Unix SMB/CIFS implementation. - RPC pipe client + SAM synchronisation and replication - Copyright (C) Tim Potter 2001 + Copyright (C) Tim Potter 2001,2002 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,6 +21,8 @@ #include "includes.h" +DOM_SID domain_sid; + static void decode_domain_info(SAM_DOMAIN_INFO *a) { fstring temp; @@ -252,20 +254,134 @@ static void decode_sam_deltas(uint32 num_deltas, SAM_DELTA_HDR *hdr_deltas, SAM_ } } +/* Convert a SAM_ACCOUNT_DELTA to a SAM_ACCOUNT. */ + +static void sam_account_from_delta(SAM_ACCOUNT *account, + SAM_ACCOUNT_INFO *delta) +{ + DOM_SID sid; + fstring s; + + /* Username, fullname, home dir, dir drive, logon script, acct + desc, workstations, profile. */ + + unistr2_to_ascii(s, &delta->uni_acct_name, sizeof(s) - 1); + pdb_set_nt_username(account, s); + + unistr2_to_ascii(s, &delta->uni_full_name, sizeof(s) - 1); + pdb_set_fullname(account, s); + + unistr2_to_ascii(s, &delta->uni_home_dir, sizeof(s) - 1); + pdb_set_homedir(account, s, True); + + unistr2_to_ascii(s, &delta->uni_dir_drive, sizeof(s) - 1); + pdb_set_dir_drive(account, s, True); + + unistr2_to_ascii(s, &delta->uni_logon_script, sizeof(s) - 1); + pdb_set_logon_script(account, s, True); + + unistr2_to_ascii(s, &delta->uni_acct_desc, sizeof(s) - 1); + pdb_set_acct_desc(account, s); + + unistr2_to_ascii(s, &delta->uni_workstations, sizeof(s) - 1); + pdb_set_workstations(account, s); + + unistr2_to_ascii(s, &delta->uni_profile, sizeof(s) - 1); + pdb_set_profile_path(account, s, True); + + /* Set Unix username to be winbindd username */ + + { + char *unix_username; + + asprintf(&unix_username, "%s%s%s", lp_workgroup(), + lp_winbind_separator(), pdb_get_nt_username(account)); + + pdb_set_username(account, unix_username); + + SAFE_FREE(unix_username); + } + + /* User and group sid */ + + sid_copy(&sid, &domain_sid); + sid_append_rid(&sid, delta->user_rid); + pdb_set_user_sid(account, &sid); + + sid_copy(&sid, &domain_sid); + sid_append_rid(&sid, delta->group_rid); + pdb_set_group_sid(account, &sid); + + /* Logon and password information */ + + pdb_set_logon_time(account, nt_time_to_unix(&delta->logon_time), True); + pdb_set_logoff_time(account, nt_time_to_unix(&delta->logoff_time), + True); + + pdb_set_logon_divs(account, delta->logon_divs); + + /* TODO: logon hours */ + /* TODO: bad password count */ + /* TODO: logon count */ + + pdb_set_pass_last_set_time( + account, nt_time_to_unix(&delta->pwd_last_set_time)); + + /* TODO: account expiry time */ + + pdb_set_acct_ctrl(account, delta->acb_info); +} + +static void apply_account_info(SAM_ACCOUNT_INFO *sam_acct_delta) +{ + SAM_ACCOUNT sam_acct; + BOOL result; + + ZERO_STRUCT(sam_acct); + + pdb_init_sam(&sam_acct); + + sam_account_from_delta(&sam_acct, sam_acct_delta); + result = pdb_add_sam_account(&sam_acct); +} + +/* Apply an array of deltas to the SAM database */ + +static void apply_deltas(uint32 num_deltas, SAM_DELTA_HDR *hdr_deltas, + SAM_DELTA_CTR *deltas) +{ + uint32 i; + + for (i = 0; i < num_deltas; i++) { + switch(hdr_deltas[i].type) { + case SAM_DELTA_ACCOUNT_INFO: + apply_account_info(&deltas[i].account_info); + break; + } + } +} + /* Synchronise sam database */ static NTSTATUS sam_sync(struct cli_state *cli, unsigned char trust_passwd[16], BOOL do_smbpasswd_output, BOOL verbose) { TALLOC_CTX *mem_ctx; - SAM_DELTA_HDR *hdr_deltas_0, *hdr_deltas_1, *hdr_deltas_2; - SAM_DELTA_CTR *deltas_0, *deltas_1, *deltas_2; - uint32 num_deltas_0, num_deltas_1, num_deltas_2; + SAM_DELTA_HDR *hdr_deltas_0, *hdr_deltas_2; + SAM_DELTA_CTR *deltas_0, *deltas_2; + uint32 num_deltas_0, num_deltas_2; NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + struct pdb_context *in; DOM_CRED ret_creds; + /* Initialise */ + if (!NT_STATUS_IS_OK(make_pdb_context_list(&in, lp_passdb_backend()))){ + DEBUG(0, ("Can't initialize passdb backend.\n")); + return result; + } + if (!(mem_ctx = talloc_init())) { DEBUG(0,("talloc_init failed\n")); return result; @@ -288,14 +404,14 @@ static NTSTATUS sam_sync(struct cli_state *cli, unsigned char trust_passwd[16], /* Do sam synchronisation on the SAM database*/ - result = cli_netlogon_sam_sync(cli, mem_ctx, &ret_creds, 0, &num_deltas_0, &hdr_deltas_0, &deltas_0); + result = cli_netlogon_sam_sync(cli, mem_ctx, &ret_creds, 0, + &num_deltas_0, &hdr_deltas_0, + &deltas_0); if (!NT_STATUS_IS_OK(result)) goto done; - /* verbose mode */ - if (verbose) - decode_sam_deltas(num_deltas_0, hdr_deltas_0, deltas_0); + apply_deltas(num_deltas_0, hdr_deltas_0, deltas_0); /* @@ -379,23 +495,6 @@ static NTSTATUS sam_repl(struct cli_state *cli, unsigned char trust_passwde[16], return result; } -/* Print usage information */ - -static void usage(void) -{ - printf("Usage: samsync [options]\n"); - - printf("\t-d debuglevel set the debuglevel\n"); - printf("\t-h Print this help message.\n"); - printf("\t-s configfile specify an alternative config file\n"); - printf("\t-S synchronise sam database\n"); - printf("\t-R replicate sam deltas\n"); - printf("\t-U username username and password\n"); - printf("\t-p produce smbpasswd output\n"); - printf("\t-V verbose output\n"); - printf("\n"); -} - /* Connect to primary domain controller */ static struct cli_state *init_connection(struct cli_state **cli, @@ -407,7 +506,16 @@ static struct cli_state *init_connection(struct cli_state **cli, int count; fstring dest_host; - /* Initialise cli_state information */ + /* Initialise myname */ + + if (!global_myname[0]) { + char *p; + + fstrcpy(global_myname, myhostname()); + p = strchr(global_myname, '.'); + if (p) + *p = 0; + } /* Look up name of PDC controller */ @@ -430,148 +538,239 @@ static struct cli_state *init_connection(struct cli_state **cli, username, domain, password, 0))) { return *cli; - } else { - return NULL; } + + return NULL; } /* Main function */ +static fstring popt_username, popt_domain, popt_password; +static BOOL popt_got_pass; + +static void user_callback(poptContext con, + enum poptCallbackReason reason, + const struct poptOption *opt, + const char *arg, const void *data) +{ + char *p, *ch; + + if (!arg) + return; + + switch(opt->val) { + + /* Check for [DOMAIN\\]username[%password]*/ + + case 'U': + + p = arg; + + if ((ch = strchr(p, '\\'))) { + fstrcpy(popt_domain, p); + popt_domain[ch - p] = 0; + } + + fstrcpy(popt_username, p); + + if ((ch = strchr(p, '%'))) { + popt_username[ch - p] = 0; + fstrcpy(popt_password, ch + 1); + popt_got_pass = True; + } + + break; + + case 'W': + fstrcpy(popt_domain, arg); + break; + } +} + +/* Return domain, username and password passed in from cmd line */ + +void popt_common_get_auth_info(char **domain, char **username, char **password, + BOOL *got_pass) +{ + *domain = popt_domain; + *username = popt_username; + *password = popt_password; + *got_pass = popt_got_pass; +} + +struct poptOption popt_common_auth_info[] = { + { NULL, 0, POPT_ARG_CALLBACK, user_callback }, + { "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set username", + "[DOMAIN\\]username[%password]" }, + { "domain", 'W', POPT_ARG_STRING, NULL, 'W', "Set domain name", + "DOMAIN"}, + POPT_TABLEEND +}; + +static BOOL popt_interactive; + +BOOL popt_common_is_interactive(void) +{ + return popt_interactive; +} + +struct poptOption popt_common_interactive[] = { + { "interactive", 'i', POPT_ARG_NONE, &popt_interactive, 'i', + "Log to stdout" }, + POPT_TABLEEND +}; + int main(int argc, char **argv) { BOOL do_sam_sync = False, do_sam_repl = False; struct cli_state *cli; NTSTATUS result; - int opt; pstring logfile; - BOOL interactive = False, do_smbpasswd_output = False; - BOOL verbose = False; - uint32 low_serial = 0; + BOOL do_smbpasswd_output = False; + BOOL verbose = True, got_pass = False; + uint32 serial = 0; unsigned char trust_passwd[16]; - fstring username, domain, password; + char *username, *domain, *password; + poptContext pc; + char c; + + struct poptOption popt_samsync_opts[] = { + { "synchronise", 'S', POPT_ARG_NONE, &do_sam_sync, 'S', + "Perform full SAM synchronisation" }, + { "replicate", 'R', POPT_ARG_NONE, &do_sam_repl, 'R', + "Replicate SAM changes" }, + { "serial", 0, POPT_ARG_INT, &serial, 0, "SAM serial number" }, + { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_debug }, + { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_auth_info }, + { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_interactive }, + POPT_AUTOHELP + POPT_TABLEEND + }; + + /* Read command line options */ + + pc = poptGetContext("samsync", argc, (const char **)argv, + popt_samsync_opts, 0); + + if (argc == 1) { + poptPrintUsage(pc, stdout, 0); + return 1; + } - if (argc == 1) { - usage(); - return 1; - } + while ((c = poptGetNextOpt(pc)) != -1) { - ZERO_STRUCT(username); - ZERO_STRUCT(domain); - ZERO_STRUCT(password); - - /* Parse command line options */ - - while((opt = getopt(argc, argv, "s:d:SR:hiU:W:pV")) != EOF) { - switch (opt) { - case 's': - pstrcpy(dyn_CONFIGFILE, optarg); - break; - case 'd': - DEBUGLEVEL = atoi(optarg); - break; - case 'S': - do_sam_sync = 1; - break; - case 'R': - do_sam_repl = 1; - low_serial = atoi(optarg); - break; - case 'i': - interactive = True; - break; - case 'U': { - char *lp; - - fstrcpy(username,optarg); - if ((lp=strchr_m(username,'%'))) { - *lp = 0; - fstrcpy(password,lp+1); - memset(strchr_m(optarg, '%') + 1, 'X', - strlen(password)); - } - break; - } - case 'W': - pstrcpy(domain, optarg); - break; - case 'p': - do_smbpasswd_output = True; - break; - case 'V': - verbose = True; - break; - case 'h': - default: - usage(); - exit(1); - } - } + /* Argument processing error */ + + if (c < -1) { + fprintf(stderr, "samsync: %s: %s\n", + poptBadOption(pc, POPT_BADOPTION_NOALIAS), + poptStrerror(c)); + return 1; + } - argc -= optind; + /* Handle arguments */ - if (argc > 0) { - usage(); - return 1; - } + switch (c) { + case 'h': + poptPrintHelp(pc, stdout, 0); + return 1; + case 'u': + poptPrintUsage(pc, stdout, 0); + return 1; + } + } + + /* Bail out if any extra args were passed */ + + if (poptPeekArg(pc)) { + fprintf(stderr, "samsync: invalid argument %s\n", + poptPeekArg(pc)); + poptPrintUsage(pc, stdout, 0); + return 1; + } + + poptFreeContext(pc); + + /* Setup logging */ - /* Initialise samba */ + dbf = x_stdout; + + if (!lp_load(dyn_CONFIGFILE, True, False, False)) { + d_fprintf(stderr, "samsync: error opening config file %s. " + "Error was %s\n", dyn_CONFIGFILE, strerror(errno)); + return 1; + } slprintf(logfile, sizeof(logfile) - 1, "%s/log.%s", dyn_LOGFILEBASE, "samsync"); + lp_set_logfile(logfile); - setup_logging("samsync", interactive); + setup_logging("samsync", popt_common_is_interactive()); - if (!interactive) + if (!popt_common_is_interactive()) reopen_logs(); - if (!lp_load(dyn_CONFIGFILE, True, False, False)) { - fprintf(stderr, "Can't load %s\n", dyn_CONFIGFILE); - } - - load_interfaces(); + load_interfaces(); /* Check arguments make sense */ if (do_sam_sync && do_sam_repl) { - fprintf(stderr, "cannot specify both -S and -R\n"); + DEBUG(0, ("cannot specify both -S and -R\n")); return 1; } if (!do_sam_sync && !do_sam_repl) { - fprintf(stderr, "must specify either -S or -R\n"); + DEBUG(0, ("samsync: you must either --synchronise or " + "--replicate the SAM database\n")); return 1; } - if (do_sam_repl && low_serial == 0) { - fprintf(stderr, "serial number must be positive\n"); + if (do_sam_repl && serial == 0) { + DEBUG(0, ("samsync: must specify serial number\n")); return 1; } + if (do_sam_sync && serial != 0) { + DEBUG(0, ("samsync: you can't specify a serial number when " + "synchonising the SAM database\n")); + return 1; + } + /* BDC operations require the machine account password */ if (!secrets_init()) { - DEBUG(0, ("Unable to initialise secrets database\n")); + DEBUG(0, ("samsync: unable to initialise secrets database\n")); return 1; } if (!secrets_fetch_trust_account_password(lp_workgroup(), trust_passwd, NULL)) { - DEBUG(0, ("could not fetch trust account password\n")); + DEBUG(0, ("samsync: could not fetch trust account password\n")); return 1; } + /* I wish the domain sid wasn't stored in secrets.tdb */ + + if (!secrets_fetch_domain_sid(lp_workgroup(), &domain_sid)) { + DEBUG(0, ("samsync: could not retrieve domain sid\n")); + return 1; + } + /* Perform sync or replication */ + popt_common_get_auth_info(&domain, &username, &password, &got_pass); + if (!init_connection(&cli, username, domain, password)) return 1; if (do_sam_sync) - result = sam_sync(cli, trust_passwd, do_smbpasswd_output, verbose); + result = sam_sync(cli, trust_passwd, do_smbpasswd_output, + verbose); if (do_sam_repl) - result = sam_repl(cli, trust_passwd, low_serial); + result = sam_repl(cli, trust_passwd, serial); if (!NT_STATUS_IS_OK(result)) { DEBUG(0, ("%s\n", nt_errstr(result))); -- cgit