Merge branch 'master' of ssh://git.samba.org/data/git/samba into selftest
[kai/samba-autobuild/.git] / source3 / utils / net_ads.c
index 5b84eb552261277bdc73d27d56baef95dd0c23d4..627374cb96bb82162a7f31f01849479ad8fb52f3 100644 (file)
 #include "includes.h"
 #include "utils/net.h"
 
-#include "libnet/libnet.h"
-
 #ifdef HAVE_ADS
 
-int net_ads_usage(struct net_context *c, int argc, const char **argv)
-{
-       d_printf("join [createupn[=principal]] [createcomputer=<org_unit>]\n");
-       d_printf("    Join the local machine to a ADS realm\n");
-       d_printf("leave\n");
-       d_printf("    Remove the local machine from a ADS realm\n");
-       d_printf("testjoin\n");
-       d_printf("    Validates the machine account in the domain\n");
-       d_printf("user\n");
-       d_printf("    List, add, or delete users in the realm\n");
-       d_printf("group\n");
-       d_printf("    List, add, or delete groups in the realm\n");
-       d_printf("info\n");
-       d_printf("    Displays details regarding a specific AD server\n");
-       d_printf("status\n");
-       d_printf("    Display details regarding the machine's account in AD\n");
-       d_printf("lookup\n");
-       d_printf("    Performs CLDAP query of AD domain controllers\n");
-       d_printf("password <username@realm> <password> -Uadmin_username@realm%%admin_pass\n");
-       d_printf("    Change a user's password using an admin account\n");
-       d_printf("    (note: use realm in UPPERCASE, prompts if password is obmitted)\n");
-       d_printf("changetrustpw\n");
-       d_printf("    Change the trust account password of this machine in the AD tree\n");
-       d_printf("printer [info | publish | remove] <printername> <servername>\n");
-       d_printf("    Lookup, add, or remove directory entry for a printer\n");
-       d_printf("{search,dn,sid}\n");
-       d_printf("    Issue LDAP search queries using a general filter, by DN, or by SID\n");
-       d_printf("keytab\n");
-       d_printf("    Manage a local keytab file based on the machine account in AD\n");
-       d_printf("dns\n");
-       d_printf("    Issue a dynamic DNS update request the server's hostname\n");
-       d_printf("    (using the machine credentials)\n");
-
-       return -1;
-}
-
 /* when we do not have sufficient input parameters to contact a remote domain
  * we always fall back to our own realm - Guenther*/
 
@@ -81,7 +43,7 @@ static const char *assume_own_realm(struct net_context *c)
 static int net_ads_cldap_netlogon(struct net_context *c, ADS_STRUCT *ads)
 {
        char addr[INET6_ADDRSTRLEN];
-       struct nbt_cldap_netlogon_5 reply;
+       struct NETLOGON_SAM_LOGON_RESPONSE_EX reply;
 
        print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
        if ( !ads_cldap_netlogon_5(talloc_tos(), addr, ads->server.realm, &reply ) ) {
@@ -93,15 +55,15 @@ static int net_ads_cldap_netlogon(struct net_context *c, ADS_STRUCT *ads)
                addr);
 
        d_printf("Response Type: ");
-       switch (reply.type) {
-       case SAMLOGON_AD_UNK_R:
-               d_printf("SAMLOGON\n");
+       switch (reply.command) {
+       case LOGON_SAM_LOGON_USER_UNKNOWN_EX:
+               d_printf("LOGON_SAM_LOGON_USER_UNKNOWN_EX\n");
                break;
-       case SAMLOGON_AD_R:
-               d_printf("SAMLOGON_USER\n");
+       case LOGON_SAM_LOGON_RESPONSE_EX:
+               d_printf("LOGON_SAM_LOGON_RESPONSE_EX\n");
                break;
        default:
-               d_printf("0x%x\n", reply.type);
+               d_printf("0x%x\n", reply.command);
                break;
        }
 
@@ -117,7 +79,9 @@ static int net_ads_cldap_netlogon(struct net_context *c, ADS_STRUCT *ads)
                 "\tIs the closest DC:                          %s\n"
                 "\tIs writable:                                %s\n"
                 "\tHas a hardware clock:                       %s\n"
-                "\tIs a non-domain NC serviced by LDAP server: %s\n",
+                "\tIs a non-domain NC serviced by LDAP server: %s\n"
+                "\tIs NT6 DC that has some secrets:            %s\n"
+                "\tIs NT6 DC that has all secrets:             %s\n",
                 (reply.server_type & NBT_SERVER_PDC) ? "yes" : "no",
                 (reply.server_type & NBT_SERVER_GC) ? "yes" : "no",
                 (reply.server_type & NBT_SERVER_LDAP) ? "yes" : "no",
@@ -127,7 +91,10 @@ static int net_ads_cldap_netlogon(struct net_context *c, ADS_STRUCT *ads)
                 (reply.server_type & NBT_SERVER_CLOSEST) ? "yes" : "no",
                 (reply.server_type & NBT_SERVER_WRITABLE) ? "yes" : "no",
                 (reply.server_type & NBT_SERVER_GOOD_TIMESERV) ? "yes" : "no",
-                (reply.server_type & DS_SERVER_NDNC) ? "yes" : "no");
+                (reply.server_type & NBT_SERVER_NDNC) ? "yes" : "no",
+                (reply.server_type & NBT_SERVER_SELECT_SECRET_DOMAIN_6) ? "yes" : "no",
+                (reply.server_type & NBT_SERVER_FULL_SECRET_DOMAIN_6) ? "yes" : "no");
+
 
        printf("Forest:\t\t\t%s\n", reply.forest);
        printf("Domain:\t\t\t%s\n", reply.dns_domain);
@@ -156,6 +123,13 @@ static int net_ads_lookup(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads lookup\n"
+                        "    Find the ADS DC using CLDAP lookup.\n");
+               return 0;
+       }
+
        if (!ADS_ERR_OK(ads_startup_nobind(c, false, &ads))) {
                d_fprintf(stderr, "Didn't find the cldap server!\n");
                return -1;
@@ -176,6 +150,14 @@ static int net_ads_info(struct net_context *c, int argc, const char **argv)
        ADS_STRUCT *ads;
        char addr[INET6_ADDRSTRLEN];
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads info\n"
+                        "    Display information about an Active Directory "
+                        "server.\n");
+               return 0;
+       }
+
        if (!ADS_ERR_OK(ads_startup_nobind(c, false, &ads))) {
                d_fprintf(stderr, "Didn't find the ldap server!\n");
                return -1;
@@ -219,11 +201,11 @@ static ADS_STATUS ads_startup_int(struct net_context *c, bool only_own_domain,
 {
        ADS_STRUCT *ads = NULL;
        ADS_STATUS status;
-       bool need_password = False;
-       bool second_time = False;
+       bool need_password = false;
+       bool second_time = false;
        char *cp;
        const char *realm = NULL;
-       bool tried_closest_dc = False;
+       bool tried_closest_dc = false;
 
        /* lp_realm() should be handled by a command line param,
           However, the join requires that realm be set in smb.conf
@@ -246,7 +228,7 @@ retry_connect:
        }
 
        if (c->opt_user_specified) {
-               need_password = True;
+               need_password = true;
        }
 
 retry:
@@ -292,8 +274,8 @@ retry:
                }
 
                if (!need_password && !second_time && !(auth_flags & ADS_AUTH_NO_BIND)) {
-                       need_password = True;
-                       second_time = True;
+                       need_password = true;
+                       second_time = true;
                        goto retry;
                } else {
                        ads_destroy(&ads);
@@ -307,7 +289,7 @@ retry:
 
        if ((only_own_domain || !c->opt_host) && !tried_closest_dc) {
 
-               tried_closest_dc = True; /* avoid loop */
+               tried_closest_dc = true; /* avoid loop */
 
                if (!ads->config.tried_closest_dc) {
 
@@ -377,7 +359,14 @@ static int net_ads_workgroup(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        char addr[INET6_ADDRSTRLEN];
-       struct nbt_cldap_netlogon_5 reply;
+       struct NETLOGON_SAM_LOGON_RESPONSE_EX reply;
+
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads workgroup\n"
+                        "    Print the workgroup name\n");
+               return 0;
+       }
 
        if (!ADS_ERR_OK(ads_startup_nobind(c, false, &ads))) {
                d_fprintf(stderr, "Didn't find the cldap server!\n");
@@ -420,21 +409,21 @@ static bool usergrp_display(ADS_STRUCT *ads, char *field, void **values, void *d
                }
                SAFE_FREE(disp_fields[0]);
                SAFE_FREE(disp_fields[1]);
-               return True;
+               return true;
        }
        if (!values) /* must be new field, indicate string field */
-               return True;
+               return true;
        if (StrCaseCmp(field, "sAMAccountName") == 0) {
                disp_fields[0] = SMB_STRDUP((char *) values[0]);
        }
        if (StrCaseCmp(field, "description") == 0)
                disp_fields[1] = SMB_STRDUP((char *) values[0]);
-       return True;
+       return true;
 }
 
 static int net_ads_user_usage(struct net_context *c, int argc, const char **argv)
 {
-       return net_help_user(c, argc, argv);
+       return net_user_usage(c, argc, argv);
 }
 
 static int ads_user_add(struct net_context *c, int argc, const char **argv)
@@ -446,7 +435,8 @@ static int ads_user_add(struct net_context *c, int argc, const char **argv)
        int rc = -1;
        char *ou_str = NULL;
 
-       if (argc < 1) return net_ads_user_usage(c, argc, argv);
+       if (argc < 1 || c->display_usage)
+               return net_ads_user_usage(c, argc, argv);
 
        if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
                return -1;
@@ -525,7 +515,7 @@ static int ads_user_info(struct net_context *c, int argc, const char **argv)
        char **grouplist;
        char *escaped_user;
 
-       if (argc < 1) {
+       if (argc < 1 || c->display_usage) {
                return net_ads_user_usage(c, argc, argv);
        }
 
@@ -612,10 +602,31 @@ static int ads_user_delete(struct net_context *c, int argc, const char **argv)
 int net_ads_user(struct net_context *c, int argc, const char **argv)
 {
        struct functable func[] = {
-               {"ADD", ads_user_add},
-               {"INFO", ads_user_info},
-               {"DELETE", ads_user_delete},
-               {NULL, NULL}
+               {
+                       "add",
+                       ads_user_add,
+                       NET_TRANSPORT_ADS,
+                       "Add an AD user",
+                       "net ads user add\n"
+                       "    Add an AD user"
+               },
+               {
+                       "info",
+                       ads_user_info,
+                       NET_TRANSPORT_ADS,
+                       "Display information about an AD user",
+                       "net ads user info\n"
+                       "    Display information about an AD user"
+               },
+               {
+                       "delete",
+                       ads_user_delete,
+                       NET_TRANSPORT_ADS,
+                       "Delete an AD user",
+                       "net ads user delete\n"
+                       "    Delete an AD user"
+               },
+               {NULL, NULL, 0, NULL, NULL}
        };
        ADS_STRUCT *ads;
        ADS_STATUS rc;
@@ -624,12 +635,20 @@ int net_ads_user(struct net_context *c, int argc, const char **argv)
        char *disp_fields[2] = {NULL, NULL};
 
        if (argc == 0) {
+               if (c->display_usage) {
+                       d_printf("Usage:\n");
+                       d_printf("net ads user\n"
+                                "    List AD users\n");
+                       net_display_usage_from_functable(func);
+                       return 0;
+               }
+
                if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
                        return -1;
                }
 
                if (c->opt_long_list_entries)
-                       d_printf("\nUser name             Comment"\
+                       d_printf("\nUser name             Comment"
                                 "\n-----------------------------\n");
 
                rc = ads_do_search_all_fn(ads, ads->config.bind_path,
@@ -642,12 +661,12 @@ int net_ads_user(struct net_context *c, int argc, const char **argv)
                return ADS_ERR_OK(rc) ? 0 : -1;
        }
 
-       return net_run_function(c, argc, argv, func, net_ads_user_usage);
+       return net_run_function(c, argc, argv, "net ads user", func);
 }
 
 static int net_ads_group_usage(struct net_context *c, int argc, const char **argv)
 {
-       return net_help_group(c, argc, argv);
+       return net_group_usage(c, argc, argv);
 }
 
 static int ads_group_add(struct net_context *c, int argc, const char **argv)
@@ -658,7 +677,7 @@ static int ads_group_add(struct net_context *c, int argc, const char **argv)
        int rc = -1;
        char *ou_str = NULL;
 
-       if (argc < 1) {
+       if (argc < 1 || c->display_usage) {
                return net_ads_group_usage(c, argc, argv);
        }
 
@@ -709,7 +728,7 @@ static int ads_group_delete(struct net_context *c, int argc, const char **argv)
        LDAPMessage *res = NULL;
        char *groupdn;
 
-       if (argc < 1) {
+       if (argc < 1 || c->display_usage) {
                return net_ads_group_usage(c, argc, argv);
        }
 
@@ -742,9 +761,23 @@ static int ads_group_delete(struct net_context *c, int argc, const char **argv)
 int net_ads_group(struct net_context *c, int argc, const char **argv)
 {
        struct functable func[] = {
-               {"ADD", ads_group_add},
-               {"DELETE", ads_group_delete},
-               {NULL, NULL}
+               {
+                       "add",
+                       ads_group_add,
+                       NET_TRANSPORT_ADS,
+                       "Add an AD group",
+                       "net ads group add\n"
+                       "    Add an AD group"
+               },
+               {
+                       "delete",
+                       ads_group_delete,
+                       NET_TRANSPORT_ADS,
+                       "Delete an AD group",
+                       "net ads group delete\n"
+                       "    Delete an AD group"
+               },
+               {NULL, NULL, 0, NULL, NULL}
        };
        ADS_STRUCT *ads;
        ADS_STATUS rc;
@@ -753,12 +786,20 @@ int net_ads_group(struct net_context *c, int argc, const char **argv)
        char *disp_fields[2] = {NULL, NULL};
 
        if (argc == 0) {
+               if (c->display_usage) {
+                       d_printf("Usage:\n");
+                       d_printf("net ads group\n"
+                                "    List AD groups\n");
+                       net_display_usage_from_functable(func);
+                       return 0;
+               }
+
                if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
                        return -1;
                }
 
                if (c->opt_long_list_entries)
-                       d_printf("\nGroup name            Comment"\
+                       d_printf("\nGroup name            Comment"
                                 "\n-----------------------------\n");
                rc = ads_do_search_all_fn(ads, ads->config.bind_path,
                                          LDAP_SCOPE_SUBTREE,
@@ -770,7 +811,7 @@ int net_ads_group(struct net_context *c, int argc, const char **argv)
                ads_destroy(&ads);
                return ADS_ERR_OK(rc) ? 0 : -1;
        }
-       return net_run_function(c, argc, argv, func, net_ads_group_usage);
+       return net_run_function(c, argc, argv, "net ads group", func);
 }
 
 static int net_ads_status(struct net_context *c, int argc, const char **argv)
@@ -779,6 +820,13 @@ static int net_ads_status(struct net_context *c, int argc, const char **argv)
        ADS_STATUS rc;
        LDAPMessage *res;
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads status\n"
+                        "    Display machine account details\n");
+               return 0;
+       }
+
        if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
                return -1;
        }
@@ -814,6 +862,13 @@ static int net_ads_leave(struct net_context *c, int argc, const char **argv)
        struct libnet_UnjoinCtx *r = NULL;
        WERROR werr;
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads leave\n"
+                        "    Leave an AD domain\n");
+               return 0;
+       }
+
        if (!*lp_realm()) {
                d_fprintf(stderr, "No realm set, are we joined ?\n");
                return -1;
@@ -824,7 +879,9 @@ static int net_ads_leave(struct net_context *c, int argc, const char **argv)
                return -1;
        }
 
-       use_in_memory_ccache();
+       if (!c->opt_kerberos) {
+               use_in_memory_ccache();
+       }
 
        werr = libnet_init_UnjoinCtx(ctx, &r);
        if (!W_ERROR_IS_OK(werr)) {
@@ -833,6 +890,7 @@ static int net_ads_leave(struct net_context *c, int argc, const char **argv)
        }
 
        r->in.debug             = true;
+       r->in.use_kerberos      = c->opt_kerberos;
        r->in.dc_name           = c->opt_host;
        r->in.domain_name       = lp_realm();
        r->in.admin_account     = c->opt_user_name;
@@ -906,6 +964,13 @@ int net_ads_testjoin(struct net_context *c, int argc, const char **argv)
        NTSTATUS status;
        use_in_memory_ccache();
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads testjoin\n"
+                        "    Test if the existing join is ok\n");
+               return 0;
+       }
+
        /* Display success or failure */
        status = net_ads_join_ok(c);
        if (!NT_STATUS_IS_OK(status)) {
@@ -1104,7 +1169,7 @@ int net_ads_join(struct net_context *c, int argc, const char **argv)
        struct libnet_JoinCtx *r = NULL;
        const char *domain = lp_realm();
        WERROR werr = WERR_SETUP_NOT_JOINED;
-       bool createupn = False;
+       bool createupn = false;
        const char *machineupn = NULL;
        const char *create_in_ou = NULL;
        int i;
@@ -1112,6 +1177,9 @@ int net_ads_join(struct net_context *c, int argc, const char **argv)
        const char *os_version = NULL;
        bool modify_config = lp_config_backend_is_registry();
 
+       if (c->display_usage)
+               return net_ads_join_usage(c, argc, argv);
+
        if (!modify_config) {
 
                werr = check_ads_config();
@@ -1127,7 +1195,9 @@ int net_ads_join(struct net_context *c, int argc, const char **argv)
                goto fail;
        }
 
-       use_in_memory_ccache();
+       if (!c->opt_kerberos) {
+               use_in_memory_ccache();
+       }
 
        werr = libnet_init_JoinCtx(ctx, &r);
        if (!W_ERROR_IS_OK(werr)) {
@@ -1138,7 +1208,7 @@ int net_ads_join(struct net_context *c, int argc, const char **argv)
 
        for ( i=0; i<argc; i++ ) {
                if ( !StrnCaseCmp(argv[i], "createupn", strlen("createupn")) ) {
-                       createupn = True;
+                       createupn = true;
                        machineupn = get_string_param(argv[i]);
                }
                else if ( !StrnCaseCmp(argv[i], "createcomputer", strlen("createcomputer")) ) {
@@ -1185,6 +1255,7 @@ int net_ads_join(struct net_context *c, int argc, const char **argv)
        r->in.admin_account     = c->opt_user_name;
        r->in.admin_password    = net_prompt_pass(c, c->opt_user_name);
        r->in.debug             = true;
+       r->in.use_kerberos      = c->opt_kerberos;
        r->in.modify_config     = modify_config;
        r->in.join_flags        = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
                                  WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE |
@@ -1228,6 +1299,7 @@ int net_ads_join(struct net_context *c, int argc, const char **argv)
                        ads_dns->auth.password = secrets_fetch_machine_password(
                                r->out.netbios_domain_name, NULL, NULL );
                        ads_dns->auth.realm = SMB_STRDUP( r->out.dns_domain_name );
+                       strupper_m(ads_dns->auth.realm );
                        ads_kinit_password( ads_dns );
                }
 
@@ -1254,23 +1326,6 @@ fail:
         return -1;
 }
 
-/*******************************************************************
- ********************************************************************/
-
-static int net_ads_dns_usage(struct net_context *c, int argc, const char **argv)
-{
-#if defined(WITH_DNS_UPDATES)
-       d_printf("net ads dns <command>\n");
-       d_printf("Valid commands:\n");
-       d_printf("   register         Issue a dynamic DNS update request for our hostname\n");
-
-       return 0;
-#else
-       d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
-       return -1;
-#endif
-}
-
 /*******************************************************************
  ********************************************************************/
 
@@ -1285,8 +1340,10 @@ static int net_ads_dns_register(struct net_context *c, int argc, const char **ar
        talloc_enable_leak_report();
 #endif
 
-       if (argc > 0) {
-               d_fprintf(stderr, "net ads dns register\n");
+       if (argc > 0 || c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads dns register\n"
+                        "    Register hostname with DNS\n");
                return -1;
        }
 
@@ -1295,7 +1352,7 @@ static int net_ads_dns_register(struct net_context *c, int argc, const char **ar
                return -1;
        }
 
-       status = ads_startup(True, &ads);
+       status = ads_startup(c, true, &ads);
        if ( !ADS_ERR_OK(status) ) {
                DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
                TALLOC_FREE(ctx);
@@ -1334,9 +1391,12 @@ static int net_ads_dns_gethostbyname(struct net_context *c, int argc, const char
        talloc_enable_leak_report();
 #endif
 
-       if (argc != 2) {
-               d_fprintf(stderr, "net ads dns gethostbyname <server> "
-                         "<name>\n");
+       if (argc != 2 || c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads dns gethostbyname <server> <name>\n"
+                        "  Look up hostname from the AD\n"
+                        "    server\tName server to use\n"
+                        "    name\tName to look up\n");
                return -1;
        }
 
@@ -1350,12 +1410,26 @@ static int net_ads_dns_gethostbyname(struct net_context *c, int argc, const char
 static int net_ads_dns(struct net_context *c, int argc, const char *argv[])
 {
        struct functable func[] = {
-               {"REGISTER", net_ads_dns_register},
-               {"GETHOSTBYNAME", net_ads_dns_gethostbyname},
-               {NULL, NULL}
+               {
+                       "register",
+                       net_ads_dns_register,
+                       NET_TRANSPORT_ADS,
+                       "Add host dns entry to AD",
+                       "net ads dns register\n"
+                       "    Add host dns entry to AD"
+               },
+               {
+                       "gethostbyname",
+                       net_ads_dns_gethostbyname,
+                       NET_TRANSPORT_ADS,
+                       "Look up host",
+                       "net ads dns gethostbyname\n"
+                       "    Look up host"
+               },
+               {NULL, NULL, 0, NULL, NULL}
        };
 
-       return net_run_function(c, argc, argv, func, net_ads_dns_usage);
+       return net_run_function(c, argc, argv, "net ads dns", func);
 }
 
 /*******************************************************************
@@ -1387,6 +1461,13 @@ static int net_ads_printer_search(struct net_context *c, int argc, const char **
        ADS_STATUS rc;
        LDAPMessage *res = NULL;
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads printer search\n"
+                        "    List printers in the AD\n");
+               return 0;
+       }
+
        if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
                return -1;
        }
@@ -1420,6 +1501,15 @@ static int net_ads_printer_info(struct net_context *c, int argc, const char **ar
        const char *servername, *printername;
        LDAPMessage *res = NULL;
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads printer info [printername [servername]]\n"
+                        "  Display printer info from AD\n"
+                        "    printername\tPrinter name or wildcard\n"
+                        "    servername\tName of the print server\n");
+               return 0;
+       }
+
        if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
                return -1;
        }
@@ -1475,14 +1565,19 @@ static int net_ads_printer_publish(struct net_context *c, int argc, const char *
        char *srv_cn_escaped = NULL, *printername_escaped = NULL;
        LDAPMessage *res = NULL;
 
-       if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
+       if (argc < 1 || c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads printer publish <printername> [servername]\n"
+                        "  Publish printer in AD\n"
+                        "    printername\tName of the printer\n"
+                        "    servername\tName of the print server\n");
                talloc_destroy(mem_ctx);
                return -1;
        }
 
-       if (argc < 1) {
+       if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
                talloc_destroy(mem_ctx);
-               return net_ads_printer_usage(c, argc, argv);
+               return -1;
        }
 
        printername = argv[0];
@@ -1544,8 +1639,8 @@ static int net_ads_printer_publish(struct net_context *c, int argc, const char *
        SAFE_FREE(srv_cn_escaped);
        SAFE_FREE(printername_escaped);
 
-       pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
-       if (!pipe_hnd) {
+       nt_status = cli_rpc_pipe_open_noauth(cli, &syntax_spoolss, &pipe_hnd);
+       if (!NT_STATUS_IS_OK(nt_status)) {
                d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
                         servername);
                SAFE_FREE(prt_dn);
@@ -1587,12 +1682,17 @@ static int net_ads_printer_remove(struct net_context *c, int argc, const char **
        char *prt_dn;
        LDAPMessage *res = NULL;
 
-       if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
+       if (argc < 1 || c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads printer remove <printername> [servername]\n"
+                        "  Remove a printer from the AD\n"
+                        "    printername\tName of the printer\n"
+                        "    servername\tName of the print server\n");
                return -1;
        }
 
-       if (argc < 1) {
-               return net_ads_printer_usage(c, argc, argv);
+       if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
+               return -1;
        }
 
        if (argc > 1) {
@@ -1635,14 +1735,42 @@ static int net_ads_printer_remove(struct net_context *c, int argc, const char **
 static int net_ads_printer(struct net_context *c, int argc, const char **argv)
 {
        struct functable func[] = {
-               {"SEARCH", net_ads_printer_search},
-               {"INFO", net_ads_printer_info},
-               {"PUBLISH", net_ads_printer_publish},
-               {"REMOVE", net_ads_printer_remove},
-               {NULL, NULL}
+               {
+                       "search",
+                       net_ads_printer_search,
+                       NET_TRANSPORT_ADS,
+                       "Search for a printer",
+                       "net ads printer search\n"
+                       "    Search for a printer"
+               },
+               {
+                       "info",
+                       net_ads_printer_info,
+                       NET_TRANSPORT_ADS,
+                       "Display printer information",
+                       "net ads printer info\n"
+                       "    Display printer information"
+               },
+               {
+                       "publish",
+                       net_ads_printer_publish,
+                       NET_TRANSPORT_ADS,
+                       "Publish a printer",
+                       "net ads printer publish\n"
+                       "    Publish a printer"
+               },
+               {
+                       "remove",
+                       net_ads_printer_remove,
+                       NET_TRANSPORT_ADS,
+                       "Delete a printer",
+                       "net ads printer remove\n"
+                       "    Delete a printer"
+               },
+               {NULL, NULL, 0, NULL, NULL}
        };
 
-       return net_run_function(c, argc, argv, func, net_ads_printer_usage);
+       return net_run_function(c, argc, argv, "net ads printer", func);
 }
 
 
@@ -1657,6 +1785,14 @@ static int net_ads_password(struct net_context *c, int argc, const char **argv)
        const char *user;
        ADS_STATUS ret;
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads password <username>\n"
+                        "  Change password for user\n"
+                        "    username\tName of user to change password for\n");
+               return 0;
+       }
+
        if (c->opt_user_name == NULL || c->opt_password == NULL) {
                d_fprintf(stderr, "You must supply an administrator username/password\n");
                return -1;
@@ -1725,6 +1861,13 @@ int net_ads_changetrustpw(struct net_context *c, int argc, const char **argv)
        fstring my_name;
        ADS_STATUS ret;
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads changetrustpw\n"
+                        "    Change the machine account's trust password\n");
+               return 0;
+       }
+
        if (!secrets_init()) {
                DEBUG(1,("Failed to initialise secrets database\n"));
                return -1;
@@ -1773,10 +1916,10 @@ int net_ads_changetrustpw(struct net_context *c, int argc, const char **argv)
 static int net_ads_search_usage(struct net_context *c, 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"\
+               "\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(c, argc, argv);
@@ -1795,7 +1938,7 @@ static int net_ads_search(struct net_context *c, int argc, const char **argv)
        const char **attrs;
        LDAPMessage *res = NULL;
 
-       if (argc < 1) {
+       if (argc < 1 || c->display_usage) {
                return net_ads_search_usage(c, argc, argv);
        }
 
@@ -1833,10 +1976,10 @@ static int net_ads_search(struct net_context *c, int argc, const char **argv)
 static int net_ads_dn_usage(struct net_context *c, int argc, const char **argv)
 {
        d_printf(
-               "\nnet ads dn <dn> <attributes...>\n"\
-               "\nperform a raw LDAP search on a ADS server and dump the results\n"\
-               "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
-               "to show in the results\n\n"\
+               "\nnet ads dn <dn> <attributes...>\n"
+               "\nperform a raw LDAP search on a ADS server and dump the results\n"
+               "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"
+               "to show in the results\n\n"
                "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
                "Note: the DN must be provided properly escaped. See RFC 4514 for details\n\n"
                );
@@ -1856,7 +1999,7 @@ static int net_ads_dn(struct net_context *c, int argc, const char **argv)
        const char **attrs;
        LDAPMessage *res = NULL;
 
-       if (argc < 1) {
+       if (argc < 1 || c->display_usage) {
                return net_ads_dn_usage(c, argc, argv);
        }
 
@@ -1893,10 +2036,10 @@ static int net_ads_dn(struct net_context *c, int argc, const char **argv)
 static int net_ads_sid_usage(struct net_context *c, int argc, const char **argv)
 {
        d_printf(
-               "\nnet ads sid <sid> <attributes...>\n"\
-               "\nperform a raw LDAP search on a ADS server and dump the results\n"\
-               "The SID is in string format, and the attributes are a list of LDAP fields \n"\
-               "to show in the results\n\n"\
+               "\nnet ads sid <sid> <attributes...>\n"
+               "\nperform a raw LDAP search on a ADS server and dump the results\n"
+               "The SID is in string format, and the attributes are a list of LDAP fields \n"
+               "to show in the results\n\n"
                "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
                );
        net_common_flags_usage(c, argc, argv);
@@ -1916,7 +2059,7 @@ static int net_ads_sid(struct net_context *c, int argc, const char **argv)
        LDAPMessage *res = NULL;
        DOM_SID sid;
 
-       if (argc < 1) {
+       if (argc < 1 || c->display_usage) {
                return net_ads_sid_usage(c, argc, argv);
        }
 
@@ -1951,34 +2094,18 @@ static int net_ads_sid(struct net_context *c, int argc, const char **argv)
        return 0;
 }
 
-
-static int net_ads_keytab_usage(struct net_context *c, int argc, const char **argv)
-{
-       d_printf(
-               "net ads keytab <COMMAND>\n"\
-"<COMMAND> can be either:\n"\
-"  ADD       Adds new service principal\n"\
-"  CREATE    Creates a fresh keytab\n"\
-"  FLUSH     Flushes out all keytab entries\n"\
-"  HELP      Prints this help message\n"\
-"  LIST      List the keytab\n"\
-"The ADD and LIST command will take arguments, the other commands\n"\
-"will not take any arguments.   The arguments given to ADD\n"\
-"should be a list of principals to add.  For example, \n"\
-"   net ads keytab add srv1 srv2\n"\
-"will add principals for the services srv1 and srv2 to the\n"\
-"system's keytab.\n"\
-"The LIST command takes a keytabname.\n"\
-"\n"
-               );
-       return -1;
-}
-
 static int net_ads_keytab_flush(struct net_context *c, int argc, const char **argv)
 {
        int ret;
        ADS_STRUCT *ads;
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads keytab flush\n"
+                        "    Delete the whole keytab\n");
+               return 0;
+       }
+
        if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
                return -1;
        }
@@ -1993,6 +2120,15 @@ static int net_ads_keytab_add(struct net_context *c, int argc, const char **argv
        int ret = 0;
        ADS_STRUCT *ads;
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads keytab add <principal> [principal ...]\n"
+                        "  Add principals to local keytab\n"
+                        "    principal\tKerberos principal to add to "
+                        "keytab\n");
+               return 0;
+       }
+
        d_printf("Processing principals to add...\n");
        if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
                return -1;
@@ -2009,6 +2145,13 @@ static int net_ads_keytab_create(struct net_context *c, int argc, const char **a
        ADS_STRUCT *ads;
        int ret;
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads keytab create\n"
+                        "    Create new default keytab\n");
+               return 0;
+       }
+
        if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
                return -1;
        }
@@ -2021,6 +2164,14 @@ static int net_ads_keytab_list(struct net_context *c, int argc, const char **arg
 {
        const char *keytab = NULL;
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads keytab list [keytab]\n"
+                        "  List a local keytab\n"
+                        "    keytab\tKeytab to list\n");
+               return 0;
+       }
+
        if (argc >= 1) {
                keytab = argv[0];
        }
@@ -2032,12 +2183,39 @@ static int net_ads_keytab_list(struct net_context *c, int argc, const char **arg
 int net_ads_keytab(struct net_context *c, int argc, const char **argv)
 {
        struct functable func[] = {
-               {"ADD", net_ads_keytab_add},
-               {"CREATE", net_ads_keytab_create},
-               {"FLUSH", net_ads_keytab_flush},
-               {"HELP", net_ads_keytab_usage},
-               {"LIST", net_ads_keytab_list},
-               {NULL, NULL}
+               {
+                       "add",
+                       net_ads_keytab_add,
+                       NET_TRANSPORT_ADS,
+                       "Add a service principal",
+                       "net ads keytab add\n"
+                       "    Add a service principal"
+               },
+               {
+                       "create",
+                       net_ads_keytab_create,
+                       NET_TRANSPORT_ADS,
+                       "Create a fresh keytab",
+                       "net ads keytab create\n"
+                       "    Create a fresh keytab"
+               },
+               {
+                       "flush",
+                       net_ads_keytab_flush,
+                       NET_TRANSPORT_ADS,
+                       "Remove all keytab entries",
+                       "net ads keytab flush\n"
+                       "    Remove all keytab entries"
+               },
+               {
+                       "list",
+                       net_ads_keytab_list,
+                       NET_TRANSPORT_ADS,
+                       "List a keytab",
+                       "net ads keytab list\n"
+                       "    List a keytab"
+               },
+               {NULL, NULL, 0, NULL, NULL}
        };
 
        if (!lp_use_kerberos_keytab()) {
@@ -2045,26 +2223,21 @@ int net_ads_keytab(struct net_context *c, int argc, const char **argv)
 use keytab functions.\n");
        }
 
-       return net_run_function(c, argc, argv, func, net_ads_keytab_usage);
+       return net_run_function(c, argc, argv, "net ads keytab", func);
 }
 
-static int net_ads_kerberos_usage(struct net_context *c, int argc, const char **argv)
+static int net_ads_kerberos_renew(struct net_context *c, int argc, const char **argv)
 {
-       d_printf(
-               "net ads kerberos <COMMAND>\n"\
-               "<COMMAND> can be either:\n"\
-               "  RENEW     Renew TGT from existing credential cache\n"\
-               "  PAC       Dumps the Kerberos PAC\n"\
-               "  KINIT     Retrieve Ticket Granting Ticket (TGT)\n"\
-               "\n"
-       );
+       int ret = -1;
 
-       return -1;
-}
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads kerberos renew\n"
+                        "    Renew TGT from existing credential cache\n");
+               return 0;
+       }
 
-static int net_ads_kerberos_renew(struct net_context *c, int argc, const char **argv)
-{
-       int ret = smb_krb5_renew_ticket(NULL, NULL, NULL, NULL);
+       ret = smb_krb5_renew_ticket(NULL, NULL, NULL, NULL);
        if (ret) {
                d_printf("failed to renew kerberos ticket: %s\n",
                        error_message(ret));
@@ -2080,6 +2253,13 @@ static int net_ads_kerberos_pac(struct net_context *c, int argc, const char **ar
        NTSTATUS status;
        int ret = -1;
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads kerberos pac\n"
+                        "    Dump the Kerberos PAC\n");
+               return 0;
+       }
+
        mem_ctx = talloc_init("net_ads_kerberos_pac");
        if (!mem_ctx) {
                goto out;
@@ -2123,6 +2303,13 @@ static int net_ads_kerberos_kinit(struct net_context *c, int argc, const char **
        int ret = -1;
        NTSTATUS status;
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads kerberos kinit\n"
+                        "    Get Ticket Granting Ticket (TGT) for the user\n");
+               return 0;
+       }
+
        mem_ctx = talloc_init("net_ads_kerberos_kinit");
        if (!mem_ctx) {
                goto out;
@@ -2151,64 +2338,195 @@ static int net_ads_kerberos_kinit(struct net_context *c, int argc, const char **
 int net_ads_kerberos(struct net_context *c, int argc, const char **argv)
 {
        struct functable func[] = {
-               {"KINIT", net_ads_kerberos_kinit},
-               {"RENEW", net_ads_kerberos_renew},
-               {"PAC", net_ads_kerberos_pac},
-               {"HELP", net_ads_kerberos_usage},
-               {NULL, NULL}
+               {
+                       "kinit",
+                       net_ads_kerberos_kinit,
+                       NET_TRANSPORT_ADS,
+                       "Retrieve Ticket Granting Ticket (TGT)",
+                       "net ads kerberos kinit\n"
+                       "    Receive Ticket Granting Ticket (TGT)"
+               },
+               {
+                       "renew",
+                       net_ads_kerberos_renew,
+                       NET_TRANSPORT_ADS,
+                       "Renew Ticket Granting Ticket from credential cache"
+                       "net ads kerberos renew\n"
+                       "    Renew Ticket Granting Ticket from credential cache"
+               },
+               {
+                       "pac",
+                       net_ads_kerberos_pac,
+                       NET_TRANSPORT_ADS,
+                       "Dump Kerberos PAC",
+                       "net ads kerberos pac\n"
+                       "    Dump Kerberos PAC"
+               },
+               {NULL, NULL, 0, NULL, NULL}
        };
 
-       return net_run_function(c, argc, argv, func, net_ads_kerberos_usage);
-}
-
-
-int net_ads_help(struct net_context *c, 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},
-               {"INFO", net_ads_info},
-               {"JOIN", net_ads_join_usage},
-               {"DNS", net_ads_dns_usage},
-               {"LEAVE", net_ads_leave},
-               {"STATUS", net_ads_status},
-               {"PASSWORD", net_ads_password},
-               {"CHANGETRUSTPW", net_ads_changetrustpw},
-               {NULL, NULL}
-       };
-
-       return net_run_function(c, argc, argv, func, net_ads_usage);
+       return net_run_function(c, argc, argv, "net ads kerberos", func);
 }
 
 int net_ads(struct net_context *c, 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},
-               {"GROUP", net_ads_group},
-               {"DNS", net_ads_dns},
-               {"PASSWORD", net_ads_password},
-               {"CHANGETRUSTPW", net_ads_changetrustpw},
-               {"PRINTER", net_ads_printer},
-               {"SEARCH", net_ads_search},
-               {"DN", net_ads_dn},
-               {"SID", net_ads_sid},
-               {"WORKGROUP", net_ads_workgroup},
-               {"LOOKUP", net_ads_lookup},
-               {"KEYTAB", net_ads_keytab},
-               {"GPO", net_ads_gpo},
-               {"KERBEROS", net_ads_kerberos},
-               {"HELP", net_ads_help},
-               {NULL, NULL}
+               {
+                       "info",
+                       net_ads_info,
+                       NET_TRANSPORT_ADS,
+                       "Display details on remote ADS server",
+                       "net ads info\n"
+                       "    Display details on remote ADS server"
+               },
+               {
+                       "join",
+                       net_ads_join,
+                       NET_TRANSPORT_ADS,
+                       "Join the local machine to ADS realm",
+                       "net ads join\n"
+                       "    Join the local machine to ADS realm"
+               },
+               {
+                       "testjoin",
+                       net_ads_testjoin,
+                       NET_TRANSPORT_ADS,
+                       "Validate machine account",
+                       "net ads testjoin\n"
+                       "    Validate machine account"
+               },
+               {
+                       "leave",
+                       net_ads_leave,
+                       NET_TRANSPORT_ADS,
+                       "Remove the local machine from ADS",
+                       "net ads leave\n"
+                       "    Remove the local machine from ADS"
+               },
+               {
+                       "status",
+                       net_ads_status,
+                       NET_TRANSPORT_ADS,
+                       "Display machine account details",
+                       "net ads status\n"
+                       "    Display machine account details"
+               },
+               {
+                       "user",
+                       net_ads_user,
+                       NET_TRANSPORT_ADS,
+                       "List/modify users",
+                       "net ads user\n"
+                       "    List/modify users"
+               },
+               {
+                       "group",
+                       net_ads_group,
+                       NET_TRANSPORT_ADS,
+                       "List/modify groups",
+                       "net ads group\n"
+                       "    List/modify groups"
+               },
+               {
+                       "dns",
+                       net_ads_dns,
+                       NET_TRANSPORT_ADS,
+                       "Issue dynamic DNS update",
+                       "net ads dns\n"
+                       "    Issue dynamic DNS update"
+               },
+               {
+                       "password",
+                       net_ads_password,
+                       NET_TRANSPORT_ADS,
+                       "Change user passwords",
+                       "net ads password\n"
+                       "    Change user passwords"
+               },
+               {
+                       "changetrustpw",
+                       net_ads_changetrustpw,
+                       NET_TRANSPORT_ADS,
+                       "Change trust account password",
+                       "net ads changetrustpw\n"
+                       "    Change trust account password"
+               },
+               {
+                       "printer",
+                       net_ads_printer,
+                       NET_TRANSPORT_ADS,
+                       "List/modify printer entries",
+                       "net ads printer\n"
+                       "    List/modify printer entries"
+               },
+               {
+                       "search",
+                       net_ads_search,
+                       NET_TRANSPORT_ADS,
+                       "Issue LDAP search using filter",
+                       "net ads search\n"
+                       "    Issue LDAP search using filter"
+               },
+               {
+                       "dn",
+                       net_ads_dn,
+                       NET_TRANSPORT_ADS,
+                       "Issue LDAP search by DN",
+                       "net ads dn\n"
+                       "    Issue LDAP search by DN"
+               },
+               {
+                       "sid",
+                       net_ads_sid,
+                       NET_TRANSPORT_ADS,
+                       "Issue LDAP search by SID",
+                       "net ads sid\n"
+                       "    Issue LDAP search by SID"
+               },
+               {
+                       "workgroup",
+                       net_ads_workgroup,
+                       NET_TRANSPORT_ADS,
+                       "Display workgroup name",
+                       "net ads workgroup\n"
+                       "    Display the workgroup name"
+               },
+               {
+                       "lookup",
+                       net_ads_lookup,
+                       NET_TRANSPORT_ADS,
+                       "Perfom CLDAP query on DC",
+                       "net ads lookup\n"
+                       "    Find the ADS DC using CLDAP lookups"
+               },
+               {
+                       "keytab",
+                       net_ads_keytab,
+                       NET_TRANSPORT_ADS,
+                       "Manage local keytab file",
+                       "net ads keytab\n"
+                       "    Manage local keytab file"
+               },
+               {
+                       "gpo",
+                       net_ads_gpo,
+                       NET_TRANSPORT_ADS,
+                       "Manage group policy objects",
+                       "net ads gpo\n"
+                       "    Manage group policy objects"
+               },
+               {
+                       "kerberos",
+                       net_ads_kerberos,
+                       NET_TRANSPORT_ADS,
+                       "Manage kerberos keytab",
+                       "net ads kerberos\n"
+                       "    Manage kerberos keytab"
+               },
+               {NULL, NULL, 0, NULL, NULL}
        };
 
-       return net_run_function(c, argc, argv, func, net_ads_usage);
+       return net_run_function(c, argc, argv, "net ads", func);
 }
 
 #else
@@ -2229,16 +2547,6 @@ int net_ads_kerberos(struct net_context *c, int argc, const char **argv)
        return net_ads_noads();
 }
 
-int net_ads_usage(struct net_context *c, int argc, const char **argv)
-{
-       return net_ads_noads();
-}
-
-int net_ads_help(struct net_context *c, int argc, const char **argv)
-{
-       return net_ads_noads();
-}
-
 int net_ads_changetrustpw(struct net_context *c, int argc, const char **argv)
 {
        return net_ads_noads();
@@ -2272,7 +2580,7 @@ int net_ads_check_our_domain(struct net_context *c)
 
 int net_ads(struct net_context *c, int argc, const char **argv)
 {
-       return net_ads_usage(c, argc, argv);
+       return net_ads_noads();
 }
 
 #endif /* WITH_ADS */