Antti Andreimann <Antti.Andreimann@mail.ee> has done some changes to enable
[tprouty/samba.git] / source / utils / net_ads.c
index ce51a50eb7faf72899ad5a7a2cbc4cf169757d86..d508320423bbe2770fd6dfe94793ba9441875a92 100644 (file)
@@ -21,6 +21,7 @@
 */
 
 #include "includes.h"
+#include "../utils/net.h"
 
 #ifdef HAVE_ADS
 
@@ -31,48 +32,89 @@ int net_ads_usage(int argc, const char **argv)
 "\n\tjoins the local machine to a ADS realm\n"\
 "\nnet ads leave"\
 "\n\tremoves the local machine from a ADS realm\n"\
+"\nnet ads testjoin"\
+"\n\ttests that an exiting join is OK\n"\
 "\nnet ads user"\
-"\n\tlist users in the realm\n"\
+"\n\tlist, add, or delete users in the realm\n"\
 "\nnet ads group"\
-"\n\tlist groups in the realm\n"\
+"\n\tlist, add, or delete groups in the realm\n"\
 "\nnet ads info"\
 "\n\tshows some info on the server\n"\
 "\nnet ads status"\
 "\n\tdump the machine account details to stdout\n"
+"\nnet ads lookup"\
+"\n\tperform a CLDAP search on the server\n"
 "\nnet ads password <username@realm> -Uadmin_username@realm%%admin_pass"\
-"\n\tchange a user's password using an admin account"
-"\n\t(note: use realm in UPPERCASE)\n"
-"\nnet ads chostpass"
-"\n\tchange the trust account password of this machine in the AD tree\n"
-"\nnet ads printer [info | publish | remove] <printername> <servername>"
-"\n\t lookup, add, or remove directory entry for a printer\n"
+"\n\tchange a user's password using an admin account"\
+"\n\t(note: use realm in UPPERCASE)\n"\
+"\nnet ads chostpass"\
+"\n\tchange the trust account password of this machine in the AD tree\n"\
+"\nnet ads printer [info | publish | remove] <printername> <servername>"\
+"\n\t lookup, add, or remove directory entry for a printer\n"\
+"\nnet ads search"\
+"\n\tperform a raw LDAP search and dump the results\n"
                );
        return -1;
 }
 
 
+/*
+  this implements the CLDAP based netlogon lookup requests
+  for finding the domain controller of a ADS domain
+*/
+static int net_ads_lookup(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+
+       ads = ads_init(NULL, NULL, opt_host);
+       if (ads) {
+               ads->auth.flags |= ADS_AUTH_NO_BIND;
+       }
+
+       ads_connect(ads);
+
+       if (!ads || !ads->config.realm) {
+               d_printf("Didn't find the cldap server!\n");
+               return -1;
+       }
+
+       return ads_cldap_netlogon(ads);
+}
+
+
+
 static int net_ads_info(int argc, const char **argv)
 {
        ADS_STRUCT *ads;
-       extern char *opt_host;
 
-       ads = ads_init(NULL, opt_host, NULL, NULL);
+       ads = ads_init(NULL, NULL, opt_host);
+
+       if (ads) {
+               ads->auth.flags |= ADS_AUTH_NO_BIND;
+       }
+
        ads_connect(ads);
 
-       if (!ads) {
+       if (!ads || !ads->config.realm) {
                d_printf("Didn't find the ldap server!\n");
                return -1;
        }
 
-       d_printf("LDAP server: %s\n", ads->ldap_server);
-       d_printf("LDAP server name: %s\n", ads->ldap_server_name);
-       d_printf("Realm: %s\n", ads->realm);
-       d_printf("Bind Path: %s\n", ads->bind_path);
+       d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
+       d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
+       d_printf("Realm: %s\n", ads->config.realm);
+       d_printf("Bind Path: %s\n", ads->config.bind_path);
        d_printf("LDAP port: %d\n", ads->ldap_port);
+       d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
 
        return 0;
 }
 
+static void use_in_memory_ccache() {
+       /* Use in-memory credentials cache so we do not interfere with
+        * existing credentials */
+       setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
+}
 
 static ADS_STRUCT *ads_startup(void)
 {
@@ -80,19 +122,17 @@ static ADS_STRUCT *ads_startup(void)
        ADS_STATUS status;
        BOOL need_password = False;
        BOOL second_time = False;
-       extern char *opt_password;
-       extern char *opt_user_name;
-       extern char *opt_host;
-       extern BOOL opt_user_specified;
        
-       ads = ads_init(NULL, opt_host, NULL, NULL);
+       ads = ads_init(NULL, NULL, opt_host);
 
        if (!opt_user_name) {
                opt_user_name = "administrator";
        }
 
-       if (opt_user_specified)
+       if (opt_user_specified) {
                need_password = True;
+               use_in_memory_ccache();
+       }
 
 retry:
        if (!opt_password && need_password) {
@@ -103,9 +143,9 @@ retry:
        }
 
        if (opt_password)
-               ads->password = strdup(opt_password);
+               ads->auth.password = strdup(opt_password);
 
-       ads->user_name = strdup(opt_user_name);
+       ads->auth.user_name = strdup(opt_user_name);
 
        status = ads_connect(ads);
        if (!ADS_ERR_OK(status)) {
@@ -114,55 +154,99 @@ retry:
                        second_time = True;
                        goto retry;
                } else {
-                       d_printf("ads_connect: %s\n", ads_errstr(status));
+                       DEBUG(1,("ads_connect: %s\n", ads_errstr(status)));
                        return NULL;
                }
        }
        return ads;
 }
 
-static void usergrp_display(char *field, void **values, void *data_area)
+
+/*
+  Check to see if connection can be made via ads.
+  ads_startup() stores the password in opt_password if it needs to so
+  that rpc or rap can use it without re-prompting.
+*/
+int net_ads_check(void)
+{
+       ADS_STRUCT *ads;
+
+       ads = ads_startup();
+       if (!ads)
+               return -1;
+       ads_destroy(&ads);
+       return 0;
+}
+
+/* 
+   determine the netbios workgroup name for a domain
+ */
+static int net_ads_workgroup(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+       TALLOC_CTX *ctx;
+       char *workgroup;
+
+       if (!(ads = ads_startup())) return -1;
+
+       if (!(ctx = talloc_init("net_ads_workgroup"))) {
+               return -1;
+       }
+
+       if (!ADS_ERR_OK(ads_workgroup_name(ads, ctx, &workgroup))) {
+               d_printf("Failed to find workgroup for realm '%s'\n", 
+                        ads->config.realm);
+               talloc_destroy(ctx);
+               return -1;
+       }
+
+       d_printf("Workgroup: %s\n", workgroup);
+
+       talloc_destroy(ctx);
+
+       return 0;
+}
+
+
+
+static BOOL usergrp_display(char *field, void **values, void *data_area)
 {
        char **disp_fields = (char **) data_area;
 
        if (!field) { /* must be end of record */
-               if (disp_fields[1])
-                       printf("%-21.21s %-50.50s\n", 
-                              disp_fields[0], disp_fields[1]);
-               else
-                       printf("%s\n", disp_fields[0]);
+               if (!strchr_m(disp_fields[0], '$')) {
+                       if (disp_fields[1])
+                               d_printf("%-21.21s %-50.50s\n", 
+                                      disp_fields[0], disp_fields[1]);
+                       else
+                               d_printf("%s\n", disp_fields[0]);
+               }
                SAFE_FREE(disp_fields[0]);
                SAFE_FREE(disp_fields[1]);
-               return;
+               return True;
        }
+       if (!values) /* must be new field, indicate string field */
+               return True;
        if (StrCaseCmp(field, "sAMAccountName") == 0) {
-               disp_fields[0] = strdup(((struct berval *) values[0])->bv_val);
+               disp_fields[0] = strdup((char *) values[0]);
        }
        if (StrCaseCmp(field, "description") == 0)
-               disp_fields[1] = strdup(((struct berval *) values[0])->bv_val);
+               disp_fields[1] = strdup((char *) values[0]);
+       return True;
 }
 
 static int net_ads_user_usage(int argc, const char **argv)
 {
-       d_printf("\nnet ads user \n\tList users\n");
-       d_printf("\nnet ads user DELETE <name>"\
-                "\n\tDelete specified user\n");
-       d_printf("\nnet ads user INFO <name>"\
-                "\n\tList the domain groups of the specified user\n");
-       d_printf("\nnet ads user ADD <name> [-F user flags]"\
-                "\n\tAdd specified user\n");
-       net_common_flags_usage(argc, argv);
-
-       return -1;
+       return net_help_user(argc, argv);
 } 
 
 static int ads_user_add(int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS status;
+       char *upn, *userdn;
        void *res=NULL;
        int rc = -1;
-       extern char *opt_comment;
 
        if (argc < 1) return net_ads_user_usage(argc, argv);
        
@@ -177,18 +261,43 @@ static int ads_user_add(int argc, const char **argv)
        
        if (ads_count_replies(ads, res)) {
                d_printf("ads_user_add: User %s already exists\n", argv[0]);
-               ads_msgfree(ads, res);
                goto done;
        }
 
-       status = ads_add_user_acct(ads, argv[0], opt_comment);
+       status = ads_add_user_acct(ads, argv[0], opt_container, opt_comment);
+
+       if (!ADS_ERR_OK(status)) {
+               d_printf("Could not add user %s: %s\n", argv[0],
+                        ads_errstr(status));
+               goto done;
+       }
+
+       /* if no password is to be set, we're done */
+       if (argc == 1) { 
+               d_printf("User %s added\n", argv[0]);
+               rc = 0;
+               goto done;
+       }
 
+       /* try setting the password */
+       asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
+       status = krb5_set_password(ads->auth.kdc_server, upn, argv[1], ads->auth.time_offset);
+       safe_free(upn);
        if (ADS_ERR_OK(status)) {
                d_printf("User %s added\n", argv[0]);
                rc = 0;
-       } else {
-               d_printf("Could not add user %s: %s\n", argv[0],
-                        ads_errstr(status));
+               goto done;
+       }
+
+       /* password didn't set, delete account */
+       d_printf("Could not add user %s.  Error setting password %s\n",
+                argv[0], ads_errstr(status));
+       ads_msgfree(ads, res);
+       status=ads_find_user_acct(ads, &res, argv[0]);
+       if (ADS_ERR_OK(status)) {
+               userdn = ads_get_dn(ads, res);
+               ads_del_dn(ads, userdn);
+               ads_memfree(ads, userdn);
        }
 
  done:
@@ -206,12 +315,18 @@ static int ads_user_info(int argc, const char **argv)
        const char *attrs[] = {"memberOf", NULL};
        char *searchstring=NULL;
        char **grouplist;
+       char *escaped_user = escape_ldap_string_alloc(argv[0]);
 
        if (argc < 1) return net_ads_user_usage(argc, argv);
        
        if (!(ads = ads_startup())) return -1;
 
-       asprintf(&searchstring, "(sAMAccountName=%s)", argv[0]);
+       if (!escaped_user) {
+               d_printf("ads_user_info: failed to escape user %s\n", argv[0]);
+               return -1;
+       }
+
+       asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
        rc = ads_search(ads, &res, searchstring, attrs);
        safe_free(searchstring);
 
@@ -227,7 +342,7 @@ static int ads_user_info(int argc, const char **argv)
                char **groupname;
                for (i=0;grouplist[i];i++) {
                        groupname = ldap_explode_dn(grouplist[i], 1);
-                       printf("%s\n", groupname[0]);
+                       d_printf("%s\n", groupname[0]);
                        ldap_value_free(groupname);
                }
                ldap_value_free(grouplist);
@@ -268,7 +383,7 @@ static int ads_user_delete(int argc, const char **argv)
        return -1;
 }
 
-static int net_ads_user(int argc, const char **argv)
+int net_ads_user(int argc, const char **argv)
 {
        struct functable func[] = {
                {"ADD", ads_user_add},
@@ -278,31 +393,23 @@ static int net_ads_user(int argc, const char **argv)
        };
        ADS_STRUCT *ads;
        ADS_STATUS rc;
-       void *res;
        const char *shortattrs[] = {"sAMAccountName", NULL};
        const char *longattrs[] = {"sAMAccountName", "description", NULL};
-       extern int opt_long_list_entries;
        char *disp_fields[2] = {NULL, NULL};
        
        if (argc == 0) {
                if (!(ads = ads_startup())) return -1;
 
-               rc = ads_do_search_all(ads, ads->bind_path, LDAP_SCOPE_SUBTREE,
-                                      "(objectclass=user)", 
-                                      opt_long_list_entries ?
-                                      longattrs : shortattrs, &res);
-
-               if (!ADS_ERR_OK(rc)) {
-                       d_printf("ads_search: %s\n", ads_errstr(rc));
-                       return -1;
-               }
-       
                if (opt_long_list_entries)
                        d_printf("\nUser name             Comment"\
                                 "\n-----------------------------\n");
-               ads_process_results(ads, res, usergrp_display, disp_fields);
-               ads_msgfree(ads, res);
 
+               rc = ads_do_search_all_fn(ads, ads->config.bind_path, 
+                                         LDAP_SCOPE_SUBTREE,
+                                         "(objectclass=user)", 
+                                         opt_long_list_entries ? longattrs :
+                                         shortattrs, usergrp_display, 
+                                         disp_fields);
                ads_destroy(&ads);
                return 0;
        }
@@ -310,54 +417,129 @@ static int net_ads_user(int argc, const char **argv)
        return net_run_function(argc, argv, func, net_ads_user_usage);
 }
 
-static int net_ads_group(int argc, const char **argv)
+static int net_ads_group_usage(int argc, const char **argv)
+{
+       return net_help_group(argc, argv);
+} 
+
+static int ads_group_add(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS status;
+       void *res=NULL;
+       int rc = -1;
+
+       if (argc < 1) return net_ads_group_usage(argc, argv);
+       
+       if (!(ads = ads_startup())) return -1;
+
+       status = ads_find_user_acct(ads, &res, argv[0]);
+
+       if (!ADS_ERR_OK(status)) {
+               d_printf("ads_group_add: %s\n", ads_errstr(status));
+               goto done;
+       }
+       
+       if (ads_count_replies(ads, res)) {
+               d_printf("ads_group_add: Group %s already exists\n", argv[0]);
+               ads_msgfree(ads, res);
+               goto done;
+       }
+
+       status = ads_add_group_acct(ads, argv[0], opt_container, opt_comment);
+
+       if (ADS_ERR_OK(status)) {
+               d_printf("Group %s added\n", argv[0]);
+               rc = 0;
+       } else {
+               d_printf("Could not add group %s: %s\n", argv[0],
+                        ads_errstr(status));
+       }
+
+ done:
+       if (res)
+               ads_msgfree(ads, res);
+       ads_destroy(&ads);
+       return rc;
+}
+
+static int ads_group_delete(int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
        void *res;
-       const char *shortattrs[] = {"sAMAccountName", NULL};
-       const char *longattrs[] = {"sAMAccountName", "description", NULL};
-       extern int opt_long_list_entries;
-       char *disp_fields[2] = {NULL, NULL};
+       char *groupdn;
 
+       if (argc < 1) return net_ads_group_usage(argc, argv);
+       
        if (!(ads = ads_startup())) return -1;
 
-       rc = ads_do_search_all(ads, ads->bind_path, LDAP_SCOPE_SUBTREE, 
-                              "(objectclass=group)", opt_long_list_entries ?
-                              longattrs : shortattrs, &res);
-
+       rc = ads_find_user_acct(ads, &res, argv[0]);
        if (!ADS_ERR_OK(rc)) {
-               d_printf("ads_search: %s\n", ads_errstr(rc));
+               DEBUG(0, ("Group %s does not exist\n", argv[0]));
                return -1;
        }
-
-       if (opt_long_list_entries)
-               d_printf("\nGroup name            Comment"\
-                        "\n-----------------------------\n");
-       ads_process_results(ads, res, usergrp_display, disp_fields);
+       groupdn = ads_get_dn(ads, res);
        ads_msgfree(ads, res);
+       rc = ads_del_dn(ads, groupdn);
+       ads_memfree(ads, groupdn);
+       if (!ADS_ERR_OK(rc)) {
+               d_printf("Group %s deleted\n", argv[0]);
+               return 0;
+       }
+       d_printf("Error deleting group %s: %s\n", argv[0], 
+                ads_errstr(rc));
+       return -1;
+}
 
-       ads_destroy(&ads);
-       return 0;
+int net_ads_group(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"ADD", ads_group_add},
+               {"DELETE", ads_group_delete},
+               {NULL, NULL}
+       };
+       ADS_STRUCT *ads;
+       ADS_STATUS rc;
+       const char *shortattrs[] = {"sAMAccountName", NULL};
+       const char *longattrs[] = {"sAMAccountName", "description", NULL};
+       char *disp_fields[2] = {NULL, NULL};
+
+       if (argc == 0) {
+               if (!(ads = ads_startup())) return -1;
+
+               if (opt_long_list_entries)
+                       d_printf("\nGroup name            Comment"\
+                                "\n-----------------------------\n");
+               rc = ads_do_search_all_fn(ads, ads->config.bind_path, 
+                                         LDAP_SCOPE_SUBTREE, 
+                                         "(objectclass=group)", 
+                                         opt_long_list_entries ? longattrs : 
+                                         shortattrs, usergrp_display, 
+                                         disp_fields);
+
+               ads_destroy(&ads);
+               return 0;
+       }
+       return net_run_function(argc, argv, func, net_ads_group_usage);
 }
 
 static int net_ads_status(int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
-       extern pstring global_myname;
        void *res;
 
        if (!(ads = ads_startup())) return -1;
 
-       rc = ads_find_machine_acct(ads, &res, global_myname);
+       rc = ads_find_machine_acct(ads, &res, global_myname());
        if (!ADS_ERR_OK(rc)) {
                d_printf("ads_find_machine_acct: %s\n", ads_errstr(rc));
                return -1;
        }
 
        if (ads_count_replies(ads, res) == 0) {
-               d_printf("No machine account for '%s' found\n", global_myname);
+               d_printf("No machine account for '%s' found\n", global_myname());
                return -1;
        }
 
@@ -370,9 +552,6 @@ static int net_ads_leave(int argc, const char **argv)
 {
        ADS_STRUCT *ads = NULL;
        ADS_STATUS rc;
-       extern pstring global_myname;
-       extern char *opt_user_name;
-       extern char *opt_password;
 
        if (!secrets_init()) {
                DEBUG(1,("Failed to initialise secrets database\n"));
@@ -380,33 +559,76 @@ static int net_ads_leave(int argc, const char **argv)
        }
 
        if (!opt_password) {
-               asprintf(&opt_user_name, "%s$", global_myname);
+               char *user_name;
+               asprintf(&user_name, "%s$", global_myname());
                opt_password = secrets_fetch_machine_password();
+               opt_user_name = user_name;
        }
 
        if (!(ads = ads_startup())) {
                return -1;
        }
 
-       rc = ads_leave_realm(ads, global_myname);
+       rc = ads_leave_realm(ads, global_myname());
        if (!ADS_ERR_OK(rc)) {
            d_printf("Failed to delete host '%s' from the '%s' realm.\n", 
-                    global_myname, ads->realm);
+                    global_myname(), ads->config.realm);
            return -1;
        }
 
-       d_printf("Removed '%s' from realm '%s'\n", global_myname, ads->realm);
+       d_printf("Removed '%s' from realm '%s'\n", global_myname(), ads->config.realm);
+
+       return 0;
+}
+
+static int net_ads_join_ok(void)
+{
+       char *user_name;
+       ADS_STRUCT *ads = NULL;
+
+       if (!secrets_init()) {
+               DEBUG(1,("Failed to initialise secrets database\n"));
+               return -1;
+       }
+
+       asprintf(&user_name, "%s$", global_myname());
+       opt_user_name = user_name;
+       opt_password = secrets_fetch_machine_password();
+
+       if (!(ads = ads_startup())) {
+               return -1;
+       }
+
+       ads_destroy(&ads);
+       return 0;
+}
+
+/*
+  check that an existing join is OK
+ */
+int net_ads_testjoin(int argc, const char **argv)
+{
+       use_in_memory_ccache();
+
+       /* Display success or failure */
+       if (net_ads_join_ok() != 0) {
+               fprintf(stderr,"Join to domain is not valid\n");
+               return -1;
+       }
 
+       printf("Join is OK\n");
        return 0;
 }
 
+/*
+  join a domain using ADS
+ */
 int net_ads_join(int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
        char *password;
        char *tmp_password;
-       extern pstring global_myname;
        const char *org_unit = "Computers";
        char *dn;
        void *res;
@@ -426,13 +648,13 @@ int net_ads_join(int argc, const char **argv)
        if (!(ads = ads_startup())) return -1;
 
        ou_str = ads_ou_string(org_unit);
-       asprintf(&dn, "%s,%s", ou_str, ads->bind_path);
+       asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path);
        free(ou_str);
 
        rc = ads_search_dn(ads, &res, dn, NULL);
        ads_msgfree(ads, res);
 
-       if (rc.error_type == ADS_ERROR_LDAP && rc.rc == LDAP_NO_SUCH_OBJECT) {
+       if (rc.error_type == ADS_ERROR_LDAP && rc.err.rc == LDAP_NO_SUCH_OBJECT) {
                d_printf("ads_join_realm: organizational unit %s does not exist (dn:%s)\n", 
                         org_unit, dn);
                return -1;
@@ -444,21 +666,21 @@ int net_ads_join(int argc, const char **argv)
                return -1;
        }       
 
-       rc = ads_join_realm(ads, global_myname, org_unit);
+       rc = ads_join_realm(ads, global_myname(), org_unit);
        if (!ADS_ERR_OK(rc)) {
                d_printf("ads_join_realm: %s\n", ads_errstr(rc));
                return -1;
        }
 
-       rc = ads_set_machine_password(ads, global_myname, password);
+       rc = ads_domain_sid(ads, &dom_sid);
        if (!ADS_ERR_OK(rc)) {
-               d_printf("ads_set_machine_password: %s\n", ads_errstr(rc));
+               d_printf("ads_domain_sid: %s\n", ads_errstr(rc));
                return -1;
        }
 
-       rc = ads_domain_sid(ads, &dom_sid);
+       rc = ads_set_machine_password(ads, global_myname(), password);
        if (!ADS_ERR_OK(rc)) {
-               d_printf("ads_domain_sid: %s\n", ads_errstr(rc));
+               d_printf("ads_set_machine_password: %s\n", ads_errstr(rc));
                return -1;
        }
 
@@ -472,7 +694,7 @@ int net_ads_join(int argc, const char **argv)
                return -1;
        }
 
-       d_printf("Joined '%s' to realm '%s'\n", global_myname, ads->realm);
+       d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm);
 
        free(password);
 
@@ -498,8 +720,7 @@ static int net_ads_printer_info(int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
-       char *servername, *printername;
-       extern pstring global_myname;
+       const char *servername, *printername;
        void *res = NULL;
 
        if (!(ads = ads_startup())) return -1;
@@ -512,7 +733,7 @@ static int net_ads_printer_info(int argc, const char **argv)
        if (argc > 1)
                servername =  argv[1];
        else
-               servername = global_myname;
+               servername = global_myname();
 
        rc = ads_find_printer_on_server(ads, &res, printername, servername);
 
@@ -534,44 +755,53 @@ static int net_ads_printer_info(int argc, const char **argv)
        return 0;
 }
 
+void do_drv_upgrade_printer(int msg_type, pid_t src, void *buf, size_t len)
+{
+       return;
+}
+
 static int net_ads_printer_publish(int argc, const char **argv)
 {
         ADS_STRUCT *ads;
         ADS_STATUS rc;
-       char *uncname, *servername;
-       ADS_PRINTER_ENTRY prt;
-       extern pstring global_myname;
-
-       /* 
-          these const strings are only here as an example.  The attributes
-          they represent are not implemented yet
-       */
-       const char *bins[] = {"Tray 21", NULL};
-       const char *media[] = {"Letter", NULL};
-       const char *orients[] = {"PORTRAIT", NULL};
-       const char *ports[] = {"Samba", NULL};
+       const char *servername;
+       struct cli_state *cli;
+       struct in_addr          server_ip;
+       NTSTATUS nt_status;
+       TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
+       ADS_MODLIST mods = ads_init_mods(mem_ctx);
+       char *prt_dn, *srv_dn, **srv_cn;
+       void *res = NULL;
 
        if (!(ads = ads_startup())) return -1;
 
        if (argc < 1)
                return net_ads_printer_usage(argc, argv);
-
-       memset(&prt, 0, sizeof(ADS_PRINTER_ENTRY));
-
-       prt.printerName = argv[0];
-       asprintf(&servername, "%s.%s", global_myname, ads->realm);
-       prt.serverName = servername;
-       prt.shortServerName = global_myname;
-       prt.versionNumber = "4";
-       asprintf(&uncname, "\\\\%s\\%s", global_myname, argv[0]);
-       prt.uNCName=uncname;
-       prt.printBinNames = (char **) bins;
-       prt.printMediaSupported = (char **) media;
-       prt.printOrientationsSupported = (char **) orients;
-       prt.portName = (char **) ports;
-       prt.printSpooling = "PrintAfterSpooled";
-        rc = ads_add_printer(ads, &prt);
+       
+       if (argc == 2)
+               servername = argv[1];
+       else
+               servername = global_myname();
+               
+       ads_find_machine_acct(ads, &res, servername);
+       srv_dn = ldap_get_dn(ads->ld, res);
+       srv_cn = ldap_explode_dn(srv_dn, 1);
+       asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn[0], argv[0], srv_dn);
+
+       resolve_name(servername, &server_ip, 0x20);
+
+       nt_status = cli_full_connection(&cli, global_myname(), servername, 
+                                       &server_ip, 0,
+                                       "IPC$", "IPC",  
+                                       opt_user_name, opt_workgroup,
+                                       opt_password ? opt_password : "", 
+                                       CLI_FULL_CONNECTION_USE_KERBEROS, 
+                                       NULL);
+
+       cli_nt_session_open(cli, PI_SPOOLSS);
+       get_remote_printer_publishing_data(cli, mem_ctx, &mods, argv[0]);
+
+        rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
         if (!ADS_ERR_OK(rc)) {
                 d_printf("ads_publish_printer: %s\n", ads_errstr(rc));
                 return -1;
@@ -586,8 +816,8 @@ static int net_ads_printer_remove(int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
-       char *servername, *prt_dn;
-       extern pstring global_myname;
+       const char *servername;
+       char *prt_dn;
        void *res = NULL;
 
        if (!(ads = ads_startup())) return -1;
@@ -598,7 +828,7 @@ static int net_ads_printer_remove(int argc, const char **argv)
        if (argc > 1)
                servername = argv[1];
        else
-               servername = global_myname;
+               servername = global_myname();
 
        rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
 
@@ -643,10 +873,8 @@ static int net_ads_printer(int argc, const char **argv)
 static int net_ads_password(int argc, const char **argv)
 {
     ADS_STRUCT *ads;
-    extern char *opt_user_name;
-    extern char *opt_password;
-    char *auth_principal = opt_user_name;
-    char *auth_password = opt_password;
+    const char *auth_principal = opt_user_name;
+    const char *auth_password = opt_password;
     char *realm = NULL;
     char *new_password = NULL;
     char *c;
@@ -659,20 +887,21 @@ static int net_ads_password(int argc, const char **argv)
        (strchr(argv[0], '@') == NULL)) {
        return net_ads_usage(argc, argv);
     }
-    
+
+    use_in_memory_ccache();    
     c = strchr(auth_principal, '@');
     realm = ++c;
 
     /* use the realm so we can eventually change passwords for users 
     in realms other than default */
-    if (!(ads = ads_init(realm, NULL, NULL, NULL))) return -1;
+    if (!(ads = ads_init(realm, NULL, NULL))) return -1;
 
     asprintf(&prompt, "Enter new password for %s:", argv[0]);
 
     new_password = getpass(prompt);
 
-    ret = kerberos_set_password(ads->kdc_server, auth_principal, 
-                               auth_password, argv[0], new_password);
+    ret = kerberos_set_password(ads->auth.kdc_server, auth_principal, 
+                               auth_password, argv[0], new_password, ads->auth.time_offset);
     if (!ADS_ERR_OK(ret)) {
        d_printf("Password change failed :-( ...\n");
        ads_destroy(&ads);
@@ -691,17 +920,30 @@ static int net_ads_password(int argc, const char **argv)
 static int net_ads_change_localhost_pass(int argc, const char **argv)
 {    
     ADS_STRUCT *ads;
-    extern pstring global_myname;
     char *host_principal;
     char *hostname;
     ADS_STATUS ret;
+    char *user_name;
+
+    if (!secrets_init()) {
+           DEBUG(1,("Failed to initialise secrets database\n"));
+           return -1;
+    }
+
+    asprintf(&user_name, "%s$", global_myname());
+    opt_user_name = user_name;
+
+    opt_password = secrets_fetch_machine_password();
 
+    use_in_memory_ccache();
 
-    if (!(ads = ads_init(NULL, NULL, NULL, NULL))) return -1;
+    if (!(ads = ads_startup())) {
+           return -1;
+    }
 
-    hostname = strdup(global_myname);
+    hostname = strdup(global_myname());
     strlower(hostname);
-    asprintf(&host_principal, "%s@%s", hostname, ads->realm);
+    asprintf(&host_principal, "%s@%s", hostname, ads->config.realm);
     SAFE_FREE(hostname);
     d_printf("Changing password for principal: HOST/%s\n", host_principal);
     
@@ -721,19 +963,79 @@ static int net_ads_change_localhost_pass(int argc, const char **argv)
     return 0;
 }
 
+/*
+  help for net ads search
+*/
+static int net_ads_search_usage(int argc, const char **argv)
+{
+       d_printf(
+               "\nnet ads search <expression> <attributes...>\n"\
+               "\nperform a raw LDAP search on a ADS server and dump the results\n"\
+               "The expression is a standard LDAP search expression, and the\n"\
+               "attributes are a list of LDAP fields to show in the results\n\n"\
+               "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
+               );
+       net_common_flags_usage(argc, argv);
+       return -1;
+}
+
+
+/*
+  general ADS search function. Useful in diagnosing problems in ADS
+*/
+static int net_ads_search(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS rc;
+       const char *exp;
+       const char **attrs;
+       void *res = NULL;
+
+       if (argc < 1) {
+               return net_ads_search_usage(argc, argv);
+       }
+
+       if (!(ads = ads_startup())) {
+               return -1;
+       }
+
+       exp = argv[0];
+       attrs = (argv + 1);
+
+       rc = ads_do_search_all(ads, ads->config.bind_path, 
+                              LDAP_SCOPE_SUBTREE,
+                              exp, attrs, &res);
+       if (!ADS_ERR_OK(rc)) {
+               d_printf("search failed: %s\n", ads_errstr(rc));
+               return -1;
+       }       
+
+       d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
+
+       /* dump the results */
+       ads_dump(ads, res);
+
+       ads_msgfree(ads, res);
+       ads_destroy(&ads);
+
+       return 0;
+}
+
+
 int net_ads_help(int argc, const char **argv)
 {
        struct functable func[] = {
                {"USER", net_ads_user_usage},
+               {"GROUP", net_ads_group_usage},
+               {"PRINTER", net_ads_printer_usage},
+               {"SEARCH", net_ads_search_usage},
 #if 0
                {"INFO", net_ads_info},
                {"JOIN", net_ads_join},
                {"LEAVE", net_ads_leave},
                {"STATUS", net_ads_status},
-               {"GROUP", net_ads_group},
                {"PASSWORD", net_ads_password},
                {"CHOSTPASS", net_ads_change_localhost_pass},
-               {"PRINTER", net_ads_printer},
 #endif
                {NULL, NULL}
        };
@@ -746,6 +1048,7 @@ int net_ads(int argc, const char **argv)
        struct functable func[] = {
                {"INFO", net_ads_info},
                {"JOIN", net_ads_join},
+               {"TESTJOIN", net_ads_testjoin},
                {"LEAVE", net_ads_leave},
                {"STATUS", net_ads_status},
                {"USER", net_ads_user},
@@ -753,6 +1056,9 @@ int net_ads(int argc, const char **argv)
                {"PASSWORD", net_ads_password},
                {"CHOSTPASS", net_ads_change_localhost_pass},
                {"PRINTER", net_ads_printer},
+               {"SEARCH", net_ads_search},
+               {"WORKGROUP", net_ads_workgroup},
+               {"LOOKUP", net_ads_lookup},
                {"HELP", net_ads_help},
                {NULL, NULL}
        };
@@ -762,19 +1068,39 @@ int net_ads(int argc, const char **argv)
 
 #else
 
-int net_ads_usage(int argc, const char **argv)
+static int net_ads_noads(void)
 {
        d_printf("ADS support not compiled in\n");
        return -1;
 }
 
+int net_ads_usage(int argc, const char **argv)
+{
+       return net_ads_noads();
+}
+
 int net_ads_help(int argc, const char **argv)
 {
-       d_printf("ADS support not compiled in\n");
-       return -1;
+       return net_ads_noads();
 }
 
 int net_ads_join(int argc, const char **argv)
+{
+       return net_ads_noads();
+}
+
+int net_ads_user(int argc, const char **argv)
+{
+       return net_ads_noads();
+}
+
+int net_ads_group(int argc, const char **argv)
+{
+       return net_ads_noads();
+}
+
+/* this one shouldn't display a message */
+int net_ads_check(void)
 {
        return -1;
 }