s3:libads: add param to prevent writing spn(s) to ads
[samba.git] / source3 / utils / net_ads.c
index ffb79991ded39d06df5fe8d446b479d294678f78..352044ed068594c32134d60b2466c93d60705aa8 100644 (file)
@@ -99,7 +99,9 @@ static int net_ads_cldap_netlogon(struct net_context *c, ADS_STRUCT *ads)
                   "\tHas a hardware clock:                       %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"),
+                  "\tIs NT6 DC that has all secrets:             %s\n"
+                  "\tRuns Active Directory Web Services:         %s\n"
+                  "\tRuns on Windows 2012 or later:              %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"),
@@ -111,7 +113,9 @@ static int net_ads_cldap_netlogon(struct net_context *c, ADS_STRUCT *ads)
                   (reply.server_type & NBT_SERVER_GOOD_TIMESERV) ? _("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"));
+                  (reply.server_type & NBT_SERVER_FULL_SECRET_DOMAIN_6) ? _("yes") : _("no"),
+                  (reply.server_type & NBT_SERVER_ADS_WEB_SERVICE) ? _("yes") : _("no"),
+                  (reply.server_type & NBT_SERVER_DS_8) ? _("yes") : _("no"));
 
 
        printf(_("Forest:\t\t\t%s\n"), reply.forest);
@@ -173,6 +177,7 @@ static int net_ads_info(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        char addr[INET6_ADDRSTRLEN];
+       time_t pass_time;
 
        if (c->display_usage) {
                d_printf("%s\n"
@@ -202,6 +207,8 @@ static int net_ads_info(struct net_context *c, int argc, const char **argv)
                d_fprintf( stderr, _("Failed to get server's current time!\n"));
        }
 
+       pass_time = secrets_fetch_pass_last_set_time(ads->server.workgroup);
+
        print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
 
        d_printf(_("LDAP server: %s\n"), addr);
@@ -215,6 +222,9 @@ static int net_ads_info(struct net_context *c, int argc, const char **argv)
        d_printf(_("KDC server: %s\n"), ads->auth.kdc_server );
        d_printf(_("Server time offset: %d\n"), ads->auth.time_offset );
 
+       d_printf(_("Last machine account password change: %s\n"),
+                http_timestring(talloc_tos(), pass_time));
+
        ads_destroy(&ads);
        return 0;
 }
@@ -226,7 +236,7 @@ static void use_in_memory_ccache(void) {
 }
 
 static ADS_STATUS ads_startup_int(struct net_context *c, bool only_own_domain,
-                                 uint32 auth_flags, ADS_STRUCT **ads_ret)
+                                 uint32_t auth_flags, ADS_STRUCT **ads_ret)
 {
        ADS_STRUCT *ads = NULL;
        ADS_STATUS status;
@@ -1115,7 +1125,7 @@ static WERROR check_ads_config( void )
        if ( lp_security() == SEC_ADS && !*lp_realm()) {
                d_fprintf(stderr, _("realm must be set in in %s for ADS "
                          "join to succeed.\n"), get_dyn_CONFIGFILE());
-               return WERR_INVALID_PARAM;
+               return WERR_INVALID_PARAMETER;
        }
 
        return WERR_OK;
@@ -1132,14 +1142,13 @@ static NTSTATUS net_update_dns_internal(struct net_context *c,
                                        TALLOC_CTX *ctx, ADS_STRUCT *ads,
                                        const char *machine_name,
                                        const struct sockaddr_storage *addrs,
-                                       int num_addrs)
+                                       int num_addrs, bool remove_host)
 {
        struct dns_rr_ns *nameservers = NULL;
        int ns_count = 0, i;
        NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
        DNS_ERROR dns_err;
        fstring dns_server;
-       const char *dns_hosts_file;
        const char *dnsdomain = NULL;
        char *root_domain = NULL;
 
@@ -1151,9 +1160,10 @@ static NTSTATUS net_update_dns_internal(struct net_context *c,
        }
        dnsdomain++;
 
-       dns_hosts_file = lp_parm_const_string(-1, "resolv", "host file", NULL);
-       status = ads_dns_lookup_ns(ctx, dns_hosts_file,
-                                  dnsdomain, &nameservers, &ns_count);
+       status = ads_dns_lookup_ns(ctx,
+                                  dnsdomain,
+                                  &nameservers,
+                                  &ns_count);
        if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
                /* Child domains often do not have NS records.  Look
                   for the NS record for the forest root domain
@@ -1191,12 +1201,17 @@ static NTSTATUS net_update_dns_internal(struct net_context *c,
 
                /* try again for NS servers */
 
-               status = ads_dns_lookup_ns(ctx, dns_hosts_file, root_domain,
-                                          &nameservers, &ns_count);
+               status = ads_dns_lookup_ns(ctx,
+                                          root_domain,
+                                          &nameservers,
+                                          &ns_count);
 
                if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
-                       DEBUG(3,("net_ads_join: Failed to find name server for the %s "
+                       DEBUG(3,("net_update_dns_internal: Failed to find name server for the %s "
                         "realm\n", ads->config.realm));
+                       if (ns_count == 0) {
+                               status = NT_STATUS_UNSUCCESSFUL;
+                       }
                        goto done;
                }
 
@@ -1206,6 +1221,25 @@ static NTSTATUS net_update_dns_internal(struct net_context *c,
 
        for (i=0; i < ns_count; i++) {
 
+               uint32_t flags = DNS_UPDATE_SIGNED |
+                                DNS_UPDATE_UNSIGNED |
+                                DNS_UPDATE_UNSIGNED_SUFFICIENT |
+                                DNS_UPDATE_PROBE |
+                                DNS_UPDATE_PROBE_SUFFICIENT;
+
+               if (c->opt_force) {
+                       flags &= ~DNS_UPDATE_PROBE_SUFFICIENT;
+                       flags &= ~DNS_UPDATE_UNSIGNED_SUFFICIENT;
+               }
+
+               /*
+                *  Do not return after PROBE completion if this function
+                *  is called for DNS removal.
+                */
+               if (remove_host) {
+                       flags &= ~DNS_UPDATE_PROBE_SUFFICIENT;
+               }
+
                status = NT_STATUS_UNSUCCESSFUL;
 
                /* Now perform the dns update - we'll try non-secure and if we fail,
@@ -1213,7 +1247,13 @@ static NTSTATUS net_update_dns_internal(struct net_context *c,
 
                fstrcpy( dns_server, nameservers[i].hostname );
 
-               dns_err = DoDNSUpdate(dns_server, dnsdomain, machine_name, addrs, num_addrs);
+               dns_err = DoDNSUpdate(dns_server,
+                                     dnsdomain,
+                                     machine_name,
+                                     addrs,
+                                     num_addrs,
+                                     flags,
+                                     remove_host);
                if (ERR_DNS_IS_OK(dns_err)) {
                        status = NT_STATUS_OK;
                        goto done;
@@ -1244,7 +1284,7 @@ static NTSTATUS net_update_dns_ext(struct net_context *c,
                                   TALLOC_CTX *mem_ctx, ADS_STRUCT *ads,
                                   const char *hostname,
                                   struct sockaddr_storage *iplist,
-                                  int num_addrs)
+                                  int num_addrs, bool remove_host)
 {
        struct sockaddr_storage *iplist_alloc = NULL;
        fstring machine_name;
@@ -1259,7 +1299,11 @@ static NTSTATUS net_update_dns_ext(struct net_context *c,
                return NT_STATUS_INVALID_PARAMETER;
        }
 
-       if (num_addrs == 0 || iplist == NULL) {
+       /*
+        * If remove_host is true, then remove all IP addresses associated with
+        * this hostname from the AD server.
+        */
+       if (!remove_host && (num_addrs == 0 || iplist == NULL)) {
                /*
                 * Get our ip address
                 * (not the 127.0.0.x address but a real ip address)
@@ -1274,7 +1318,7 @@ static NTSTATUS net_update_dns_ext(struct net_context *c,
        }
 
        status = net_update_dns_internal(c, mem_ctx, ads, machine_name,
-                                        iplist, num_addrs);
+                                        iplist, num_addrs, remove_host);
 
        SAFE_FREE(iplist_alloc);
        return status;
@@ -1284,7 +1328,7 @@ static NTSTATUS net_update_dns(struct net_context *c, TALLOC_CTX *mem_ctx, ADS_S
 {
        NTSTATUS status;
 
-       status = net_update_dns_ext(c, mem_ctx, ads, hostname, NULL, 0);
+       status = net_update_dns_ext(c, mem_ctx, ads, hostname, NULL, 0, false);
        return status;
 }
 #endif
@@ -1295,21 +1339,29 @@ static NTSTATUS net_update_dns(struct net_context *c, TALLOC_CTX *mem_ctx, ADS_S
 
 static int net_ads_join_usage(struct net_context *c, int argc, const char **argv)
 {
-       d_printf(_("net ads join [options]\n"
+       d_printf(_("net ads join [--no-dns-updates] [options]\n"
                   "Valid options:\n"));
-       d_printf(_("   createupn[=UPN]    Set the userPrincipalName attribute during the join.\n"
-                  "                      The deault UPN is in the form host/netbiosname@REALM.\n"));
-       d_printf(_("   createcomputer=OU  Precreate the computer account in a specific OU.\n"
-                  "                      The OU string read from top to bottom without RDNs and delimited by a '/'.\n"
-                  "                      E.g. \"createcomputer=Computers/Servers/Unix\"\n"
-                  "                      NB: A backslash '\\' is used as escape at multiple levels and may\n"
-                  "                          need to be doubled or even quadrupled.  It is not used as a separator.\n"));
-       d_printf(_("   osName=string      Set the operatingSystem attribute during the join.\n"));
-       d_printf(_("   osVer=string       Set the operatingSystemVersion attribute during the join.\n"
-                  "                      NB: osName and osVer must be specified together for either to take effect.\n"
-                  "                          Also, the operatingSystemService attribute is also set when along with\n"
-                  "                          the two other attributes.\n"));
-
+       d_printf(_("   createupn[=UPN]       Set the userPrincipalName attribute during the join.\n"
+                  "                         The default UPN is in the form host/netbiosname@REALM.\n"));
+       d_printf(_("   createcomputer=OU     Precreate the computer account in a specific OU.\n"
+                  "                         The OU string read from top to bottom without RDNs\n"
+                  "                         and delimited by a '/'.\n"
+                  "                         E.g. \"createcomputer=Computers/Servers/Unix\"\n"
+                  "                         NB: A backslash '\\' is used as escape at multiple\n"
+                  "                             levels and may need to be doubled or even\n"
+                  "                             quadrupled. It is not used as a separator.\n"));
+       d_printf(_("   machinepass=PASS      Set the machine password to a specific value during\n"
+                  "                         the join. The default password is random.\n"));
+       d_printf(_("   osName=string         Set the operatingSystem attribute during the join.\n"));
+       d_printf(_("   osVer=string          Set the operatingSystemVersion attribute during join.\n"
+                  "                         NB: osName and osVer must be specified together for\n"
+                  "                             either to take effect. The operatingSystemService\n"
+                  "                             attribute is then also set along with the two\n"
+                  "                             other attributes.\n"));
+       d_printf(_("   osServicePack=string  Set the operatingSystemServicePack attribute\n"
+                  "                         during the join.\n"
+                  "                         NB: If not specified then by default the samba\n"
+                  "                             version string is used instead.\n"));
        return -1;
 }
 
@@ -1407,14 +1459,17 @@ int net_ads_join(struct net_context *c, int argc, const char **argv)
        TALLOC_CTX *ctx = NULL;
        struct libnet_JoinCtx *r = NULL;
        const char *domain = lp_realm();
-       WERROR werr = WERR_SETUP_NOT_JOINED;
+       WERROR werr = WERR_NERR_SETUPNOTJOINED;
        bool createupn = false;
        const char *machineupn = NULL;
+       const char *machine_password = NULL;
        const char *create_in_ou = NULL;
        int i;
        const char *os_name = NULL;
        const char *os_version = NULL;
+       const char *os_servicepack = NULL;
        bool modify_config = lp_config_backend_is_registry();
+       enum libnetjoin_JoinDomNameType domain_name_type = JoinDomNameTypeDNS;
 
        if (c->display_usage)
                return net_ads_join_usage(c, argc, argv);
@@ -1430,7 +1485,7 @@ int net_ads_join(struct net_context *c, int argc, const char **argv)
 
        if (!(ctx = talloc_init("net_ads_join"))) {
                d_fprintf(stderr, _("Could not initialise talloc context.\n"));
-               werr = WERR_NOMEM;
+               werr = WERR_NOT_ENOUGH_MEMORY;
                goto fail;
        }
 
@@ -1453,32 +1508,51 @@ int net_ads_join(struct net_context *c, int argc, const char **argv)
                else if ( !strncasecmp_m(argv[i], "createcomputer", strlen("createcomputer")) ) {
                        if ( (create_in_ou = get_string_param(argv[i])) == NULL ) {
                                d_fprintf(stderr, _("Please supply a valid OU path.\n"));
-                               werr = WERR_INVALID_PARAM;
+                               werr = WERR_INVALID_PARAMETER;
                                goto fail;
                        }
                }
                else if ( !strncasecmp_m(argv[i], "osName", strlen("osName")) ) {
                        if ( (os_name = get_string_param(argv[i])) == NULL ) {
                                d_fprintf(stderr, _("Please supply a operating system name.\n"));
-                               werr = WERR_INVALID_PARAM;
+                               werr = WERR_INVALID_PARAMETER;
                                goto fail;
                        }
                }
                else if ( !strncasecmp_m(argv[i], "osVer", strlen("osVer")) ) {
                        if ( (os_version = get_string_param(argv[i])) == NULL ) {
                                d_fprintf(stderr, _("Please supply a valid operating system version.\n"));
-                               werr = WERR_INVALID_PARAM;
+                               werr = WERR_INVALID_PARAMETER;
+                               goto fail;
+                       }
+               }
+               else if ( !strncasecmp_m(argv[i], "osServicePack", strlen("osServicePack")) ) {
+                       if ( (os_servicepack = get_string_param(argv[i])) == NULL ) {
+                               d_fprintf(stderr, _("Please supply a valid servicepack identifier.\n"));
+                               werr = WERR_INVALID_PARAMETER;
+                               goto fail;
+                       }
+               }
+               else if ( !strncasecmp_m(argv[i], "machinepass", strlen("machinepass")) ) {
+                       if ( (machine_password = get_string_param(argv[i])) == NULL ) {
+                               d_fprintf(stderr, _("Please supply a valid password to set as trust account password.\n"));
+                               werr = WERR_INVALID_PARAMETER;
                                goto fail;
                        }
                }
                else {
                        domain = argv[i];
+                       if (strchr(domain, '.') == NULL) {
+                               domain_name_type = JoinDomNameTypeUnknown;
+                       } else {
+                               domain_name_type = JoinDomNameTypeDNS;
+                       }
                }
        }
 
        if (!*domain) {
                d_fprintf(stderr, _("Please supply a valid domain name\n"));
-               werr = WERR_INVALID_PARAM;
+               werr = WERR_INVALID_PARAMETER;
                goto fail;
        }
 
@@ -1492,14 +1566,17 @@ int net_ads_join(struct net_context *c, int argc, const char **argv)
        /* Do the domain join here */
 
        r->in.domain_name       = domain;
+       r->in.domain_name_type  = domain_name_type;
        r->in.create_upn        = createupn;
        r->in.upn               = machineupn;
        r->in.account_ou        = create_in_ou;
        r->in.os_name           = os_name;
        r->in.os_version        = os_version;
+       r->in.os_servicepack    = os_servicepack;
        r->in.dc_name           = c->opt_host;
        r->in.admin_account     = c->opt_user_name;
        r->in.admin_password    = net_prompt_pass(c, c->opt_user_name);
+       r->in.machine_password  = machine_password;
        r->in.debug             = true;
        r->in.use_kerberos      = c->opt_kerberos;
        r->in.modify_config     = modify_config;
@@ -1509,9 +1586,10 @@ int net_ads_join(struct net_context *c, int argc, const char **argv)
        r->in.msg_ctx           = c->msg_ctx;
 
        werr = libnet_Join(ctx, r);
-       if (W_ERROR_EQUAL(werr, WERR_DCNOTFOUND) &&
+       if (W_ERROR_EQUAL(werr, WERR_NERR_DCNOTFOUND) &&
            strequal(domain, lp_realm())) {
                r->in.domain_name = lp_workgroup();
+               r->in.domain_name_type = JoinDomNameTypeNBT;
                werr = libnet_Join(ctx, r);
        }
        if (!W_ERROR_IS_OK(werr)) {
@@ -1532,19 +1610,27 @@ int net_ads_join(struct net_context *c, int argc, const char **argv)
        d_printf(_("Using short domain name -- %s\n"), r->out.netbios_domain_name);
 
        if (r->out.dns_domain_name) {
-               d_printf(_("Joined '%s' to realm '%s'\n"), r->in.machine_name,
+               d_printf(_("Joined '%s' to dns domain '%s'\n"), r->in.machine_name,
                        r->out.dns_domain_name);
        } else {
                d_printf(_("Joined '%s' to domain '%s'\n"), r->in.machine_name,
                        r->out.netbios_domain_name);
        }
 
+       /* print out informative error string in case there is one */
+       if (r->out.error_string != NULL) {
+               d_printf("%s\n", r->out.error_string);
+       }
+
        /*
-        * We try doing the dns update (if it was compiled in).
+        * We try doing the dns update (if it was compiled in
+        * and if it was not disabled on the command line).
         * If the dns update fails, we still consider the join
         * operation as succeeded if we came this far.
         */
-       _net_ads_join_dns_updates(c, ctx, r);
+       if (!c->opt_no_dns_updates) {
+               _net_ads_join_dns_updates(c, ctx, r);
+       }
 
        TALLOC_FREE(r);
        TALLOC_FREE( ctx );
@@ -1640,7 +1726,7 @@ static int net_ads_dns_register(struct net_context *c, int argc, const char **ar
                return -1;
        }
 
-       ntstatus = net_update_dns_ext(c, ctx, ads, hostname, addrs, num_addrs);
+       ntstatus = net_update_dns_ext(c, ctx, ads, hostname, addrs, num_addrs, false);
        if (!NT_STATUS_IS_OK(ntstatus)) {
                d_fprintf( stderr, _("DNS update failed!\n") );
                ads_destroy( &ads );
@@ -1661,6 +1747,70 @@ static int net_ads_dns_register(struct net_context *c, int argc, const char **ar
 #endif
 }
 
+static int net_ads_dns_unregister(struct net_context *c,
+                                 int argc,
+                                 const char **argv)
+{
+#if defined(WITH_DNS_UPDATES)
+       ADS_STRUCT *ads;
+       ADS_STATUS status;
+       NTSTATUS ntstatus;
+       TALLOC_CTX *ctx;
+       const char *hostname = NULL;
+
+#ifdef DEVELOPER
+       talloc_enable_leak_report();
+#endif
+
+       if (argc != 1) {
+               c->display_usage = true;
+       }
+
+       if (c->display_usage) {
+               d_printf(  "%s\n"
+                          "net ads dns unregister [hostname]\n"
+                          "    %s\n",
+                        _("Usage:"),
+                        _("Register hostname with DNS\n"));
+               return -1;
+       }
+
+       if (!(ctx = talloc_init("net_ads_dns"))) {
+               d_fprintf(stderr, _("Could not initialise talloc context\n"));
+               return -1;
+       }
+
+       /* Get the hostname for un-registering */
+       hostname = argv[0];
+
+       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);
+               return -1;
+       }
+
+       ntstatus = net_update_dns_ext(c, ctx, ads, hostname, NULL, 0, true);
+       if (!NT_STATUS_IS_OK(ntstatus)) {
+               d_fprintf( stderr, _("DNS update failed!\n") );
+               ads_destroy( &ads );
+               TALLOC_FREE( ctx );
+               return -1;
+       }
+
+       d_fprintf( stderr, _("Successfully un-registered hostname from DNS\n"));
+
+       ads_destroy(&ads);
+       TALLOC_FREE( ctx );
+
+       return 0;
+#else
+       d_fprintf(stderr,
+                 _("DNS update support not enabled at compile time!\n"));
+       return -1;
+#endif
+}
+
 static int net_ads_dns_gethostbyname(struct net_context *c, int argc, const char **argv)
 {
 #if defined(WITH_DNS_UPDATES)
@@ -1683,9 +1833,10 @@ static int net_ads_dns_gethostbyname(struct net_context *c, int argc, const char
        }
 
        err = do_gethostbyname(argv[0], argv[1]);
-
-       d_printf(_("do_gethostbyname returned %s (%d)\n"),
-               dns_errstr(err), ERROR_DNS_V(err));
+       if (!ERR_DNS_IS_OK(err)) {
+               d_printf(_("do_gethostbyname returned %s (%d)\n"),
+                       dns_errstr(err), ERROR_DNS_V(err));
+       }
 #endif
        return 0;
 }
@@ -1701,6 +1852,14 @@ static int net_ads_dns(struct net_context *c, int argc, const char *argv[])
                        N_("net ads dns register\n"
                           "    Add host dns entry to AD")
                },
+               {
+                       "unregister",
+                       net_ads_dns_unregister,
+                       NET_TRANSPORT_ADS,
+                       N_("Remove host dns entry from AD"),
+                       N_("net ads dns unregister\n"
+                          "    Remove host dns entry from AD")
+               },
                {
                        "gethostbyname",
                        net_ads_dns_gethostbyname,
@@ -1850,6 +2009,7 @@ static int net_ads_printer_publish(struct net_context *c, int argc, const char *
        char *prt_dn, *srv_dn, **srv_cn;
        char *srv_cn_escaped = NULL, *printername_escaped = NULL;
        LDAPMessage *res = NULL;
+       bool ok;
 
        if (argc < 1 || c->display_usage) {
                d_printf("%s\n%s",
@@ -1877,7 +2037,14 @@ static int net_ads_printer_publish(struct net_context *c, int argc, const char *
 
        /* Get printer data from SPOOLSS */
 
-       resolve_name(servername, &server_ss, 0x20, false);
+       ok = resolve_name(servername, &server_ss, 0x20, false);
+       if (!ok) {
+               d_fprintf(stderr, _("Could not find server %s\n"),
+                         servername);
+               ads_destroy(&ads);
+               talloc_destroy(mem_ctx);
+               return -1;
+       }
 
        nt_status = cli_full_connection(&cli, lp_netbios_name(), servername,
                                        &server_ss, 0,
@@ -1885,7 +2052,7 @@ static int net_ads_printer_publish(struct net_context *c, int argc, const char *
                                        c->opt_user_name, c->opt_workgroup,
                                        c->opt_password ? c->opt_password : "",
                                        CLI_FULL_CONNECTION_USE_KERBEROS,
-                                       SMB_SIGNING_DEFAULT);
+                                       SMB_SIGNING_IPC_DEFAULT);
 
        if (NT_STATUS_IS_ERR(nt_status)) {
                d_fprintf(stderr, _("Unable to open a connection to %s to "
@@ -1935,7 +2102,7 @@ static int net_ads_printer_publish(struct net_context *c, int argc, const char *
        SAFE_FREE(srv_cn_escaped);
        SAFE_FREE(printername_escaped);
 
-       nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_spoolss.syntax_id, &pipe_hnd);
+       nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_spoolss, &pipe_hnd);
        if (!NT_STATUS_IS_OK(nt_status)) {
                d_fprintf(stderr, _("Unable to open a connection to the spoolss pipe on %s\n"),
                         servername);
@@ -2080,6 +2247,7 @@ static int net_ads_password(struct net_context *c, int argc, const char **argv)
        const char *new_password = NULL;
        char *chr, *prompt;
        const char *user;
+       char pwd[256] = {0};
        ADS_STATUS ret;
 
        if (c->display_usage) {
@@ -2138,15 +2306,22 @@ static int net_ads_password(struct net_context *c, int argc, const char **argv)
        if (argv[1]) {
                new_password = (const char *)argv[1];
        } else {
+               int rc;
+
                if (asprintf(&prompt, _("Enter new password for %s:"), user) == -1) {
                        return -1;
                }
-               new_password = getpass(prompt);
+               rc = samba_getpass(prompt, pwd, sizeof(pwd), false, true);
+               if (rc < 0) {
+                       return -1;
+               }
+               new_password = pwd;
                free(prompt);
        }
 
        ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
                                auth_password, user, new_password, ads->auth.time_offset);
+       memset(pwd, '\0', sizeof(pwd));
        if (!ADS_ERR_OK(ret)) {
                d_fprintf(stderr, _("Password change failed: %s\n"), ads_errstr(ret));
                ads_destroy(&ads);
@@ -2451,7 +2626,7 @@ static int net_ads_keytab_add(struct net_context *c, int argc, const char **argv
                return -1;
        }
        for (i = 0; i < argc; i++) {
-               ret |= ads_keytab_add_entry(ads, argv[i]);
+               ret |= ads_keytab_add_entry(ads, argv[i], false);
        }
        ads_destroy(&ads);
        return ret;
@@ -2567,35 +2742,41 @@ static int net_ads_kerberos_renew(struct net_context *c, int argc, const char **
        return ret;
 }
 
-static int net_ads_kerberos_pac(struct net_context *c, int argc, const char **argv)
+static int net_ads_kerberos_pac_common(struct net_context *c, int argc, const char **argv,
+                                      struct PAC_DATA_CTR **pac_data_ctr)
 {
-       struct PAC_LOGON_INFO *info = NULL;
-       TALLOC_CTX *mem_ctx = NULL;
        NTSTATUS status;
        int ret = -1;
        const char *impersonate_princ_s = NULL;
+       const char *local_service = NULL;
+       int i;
 
-       if (c->display_usage) {
-               d_printf(  "%s\n"
-                          "net ads kerberos pac\n"
-                          "    %s\n",
-                        _("Usage:"),
-                        _("Dump the Kerberos PAC"));
-               return 0;
-       }
-
-       mem_ctx = talloc_init("net_ads_kerberos_pac");
-       if (!mem_ctx) {
-               goto out;
+       for (i=0; i<argc; i++) {
+               if (strnequal(argv[i], "impersonate", strlen("impersonate"))) {
+                       impersonate_princ_s = get_string_param(argv[i]);
+                       if (impersonate_princ_s == NULL) {
+                               return -1;
+                       }
+               }
+               if (strnequal(argv[i], "local_service", strlen("local_service"))) {
+                       local_service = get_string_param(argv[i]);
+                       if (local_service == NULL) {
+                               return -1;
+                       }
+               }
        }
 
-       if (argc > 0) {
-               impersonate_princ_s = argv[0];
+       if (local_service == NULL) {
+               local_service = talloc_asprintf(c, "%s$@%s",
+                                               lp_netbios_name(), lp_realm());
+               if (local_service == NULL) {
+                       goto out;
+               }
        }
 
        c->opt_password = net_prompt_pass(c, c->opt_user_name);
 
-       status = kerberos_return_pac(mem_ctx,
+       status = kerberos_return_pac(c,
                                     c->opt_user_name,
                                     c->opt_password,
                                     0,
@@ -2606,25 +2787,150 @@ static int net_ads_kerberos_pac(struct net_context *c, int argc, const char **ar
                                     true,
                                     2592000, /* one month */
                                     impersonate_princ_s,
-                                    &info);
+                                    local_service,
+                                    pac_data_ctr);
        if (!NT_STATUS_IS_OK(status)) {
                d_printf(_("failed to query kerberos PAC: %s\n"),
                        nt_errstr(status));
                goto out;
        }
 
-       if (info) {
-               const char *s;
-               s = NDR_PRINT_STRUCT_STRING(mem_ctx, PAC_LOGON_INFO, info);
-               d_printf(_("The Pac: %s\n"), s);
-       }
-
        ret = 0;
  out:
-       TALLOC_FREE(mem_ctx);
        return ret;
 }
 
+static int net_ads_kerberos_pac_dump(struct net_context *c, int argc, const char **argv)
+{
+       struct PAC_DATA_CTR *pac_data_ctr = NULL;
+       int i;
+       int ret = -1;
+       enum PAC_TYPE type = 0;
+
+       if (c->display_usage) {
+               d_printf(  "%s\n"
+                          "net ads kerberos pac dump [impersonate=string] [local_service=string] [pac_buffer_type=int]\n"
+                          "    %s\n",
+                        _("Usage:"),
+                        _("Dump the Kerberos PAC"));
+               return -1;
+       }
+
+       for (i=0; i<argc; i++) {
+               if (strnequal(argv[i], "pac_buffer_type", strlen("pac_buffer_type"))) {
+                       type = get_int_param(argv[i]);
+               }
+       }
+
+       ret = net_ads_kerberos_pac_common(c, argc, argv, &pac_data_ctr);
+       if (ret) {
+               return ret;
+       }
+
+       if (type == 0) {
+
+               char *s = NULL;
+
+               s = NDR_PRINT_STRUCT_STRING(c, PAC_DATA,
+                       pac_data_ctr->pac_data);
+               if (s != NULL) {
+                       d_printf(_("The Pac: %s\n"), s);
+                       talloc_free(s);
+               }
+
+               return 0;
+       }
+
+       for (i=0; i < pac_data_ctr->pac_data->num_buffers; i++) {
+
+               char *s = NULL;
+
+               if (pac_data_ctr->pac_data->buffers[i].type != type) {
+                       continue;
+               }
+
+               s = NDR_PRINT_UNION_STRING(c, PAC_INFO, type,
+                               pac_data_ctr->pac_data->buffers[i].info);
+               if (s != NULL) {
+                       d_printf(_("The Pac: %s\n"), s);
+                       talloc_free(s);
+               }
+               break;
+       }
+
+       return 0;
+}
+
+static int net_ads_kerberos_pac_save(struct net_context *c, int argc, const char **argv)
+{
+       struct PAC_DATA_CTR *pac_data_ctr = NULL;
+       char *filename = NULL;
+       int ret = -1;
+       int i;
+
+       if (c->display_usage) {
+               d_printf(  "%s\n"
+                          "net ads kerberos pac save [impersonate=string] [local_service=string] [filename=string]\n"
+                          "    %s\n",
+                        _("Usage:"),
+                        _("Save the Kerberos PAC"));
+               return -1;
+       }
+
+       for (i=0; i<argc; i++) {
+               if (strnequal(argv[i], "filename", strlen("filename"))) {
+                       filename = get_string_param(argv[i]);
+                       if (filename == NULL) {
+                               return -1;
+                       }
+               }
+       }
+
+       ret = net_ads_kerberos_pac_common(c, argc, argv, &pac_data_ctr);
+       if (ret) {
+               return ret;
+       }
+
+       if (filename == NULL) {
+               d_printf(_("please define \"filename=<filename>\" to save the PAC\n"));
+               return -1;
+       }
+
+       /* save the raw format */
+       if (!file_save(filename, pac_data_ctr->pac_blob.data, pac_data_ctr->pac_blob.length)) {
+               d_printf(_("failed to save PAC in %s\n"), filename);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int net_ads_kerberos_pac(struct net_context *c, int argc, const char **argv)
+{
+       struct functable func[] = {
+               {
+                       "dump",
+                       net_ads_kerberos_pac_dump,
+                       NET_TRANSPORT_ADS,
+                       N_("Dump Kerberos PAC"),
+                       N_("net ads kerberos pac dump\n"
+                          "    Dump a Kerberos PAC to stdout")
+               },
+               {
+                       "save",
+                       net_ads_kerberos_pac_save,
+                       NET_TRANSPORT_ADS,
+                       N_("Save Kerberos PAC"),
+                       N_("net ads kerberos pac save\n"
+                          "    Save a Kerberos PAC in a file")
+               },
+
+               {NULL, NULL, 0, NULL, NULL}
+       };
+
+       return net_run_function(c, argc, argv, "net ads kerberos pac", func);
+}
+
 static int net_ads_kerberos_kinit(struct net_context *c, int argc, const char **argv)
 {
        TALLOC_CTX *mem_ctx = NULL;
@@ -2699,6 +3005,431 @@ int net_ads_kerberos(struct net_context *c, int argc, const char **argv)
        return net_run_function(c, argc, argv, "net ads kerberos", func);
 }
 
+static int net_ads_setspn_list(struct net_context *c, int argc, const char **argv)
+{
+       int ret = 0;
+       bool ok = false;
+       ADS_STRUCT *ads = NULL;
+       if (c->display_usage) {
+               d_printf("%s\n%s",
+                        _("Usage:"),
+                        _("net ads setspn list <machinename>\n"));
+               ret = 0;
+               goto done;
+       }
+       if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
+               ret = -1;
+               goto done;
+       }
+       if (argc) {
+               ok = ads_setspn_list(ads, argv[0]);
+       } else {
+               ok = ads_setspn_list(ads, lp_netbios_name());
+       }
+       if (!ok) {
+            ret = -1;
+       }
+done:
+       if (ads) {
+               ads_destroy(&ads);
+       }
+       return ret;
+}
+
+static int net_ads_setspn_add(struct net_context *c, int argc, const char **argv)
+{
+       int ret = 0;
+       bool ok = false;
+       ADS_STRUCT *ads = NULL;
+       if (c->display_usage || argc < 1) {
+               d_printf("%s\n%s",
+                        _("Usage:"),
+                        _("net ads setspn add <machinename> SPN\n"));
+               ret = 0;
+               goto done;
+       }
+       if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
+               ret = -1;
+               goto done;
+       }
+       if (argc > 1) {
+               ok = ads_setspn_add(ads, argv[0], argv[1]);
+       } else {
+               ok = ads_setspn_add(ads, lp_netbios_name(), argv[0]);
+       }
+       if (!ok) {
+            ret = -1;
+       }
+done:
+       if (ads) {
+               ads_destroy(&ads);
+       }
+       return ret;
+}
+
+static int net_ads_setspn_delete(struct net_context *c, int argc, const char **argv)
+{
+       int ret = 0;
+       bool ok = false;
+       ADS_STRUCT *ads = NULL;
+       if (c->display_usage || argc < 1) {
+               d_printf("%s\n%s",
+                        _("Usage:"),
+                        _("net ads setspn delete <machinename> SPN\n"));
+               ret = 0;
+               goto done;
+       }
+       if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
+               ret = -1;
+               goto done;
+       }
+       if (argc > 1) {
+               ok = ads_setspn_delete(ads, argv[0], argv[1]);
+       } else {
+               ok = ads_setspn_delete(ads, lp_netbios_name(), argv[0]);
+       }
+       if (!ok) {
+               ret = -1;
+       }
+done:
+       if (ads) {
+               ads_destroy(&ads);
+       }
+       return ret;
+}
+
+int net_ads_setspn(struct net_context *c, int argc, const char **argv)
+{
+       struct functable func[] = {
+               {
+                       "list",
+                       net_ads_setspn_list,
+                       NET_TRANSPORT_ADS,
+                       N_("List Service Principal Names (SPN)"),
+                       N_("net ads setspn list machine\n"
+                          "    List Service Principal Names (SPN)")
+               },
+               {
+                       "add",
+                       net_ads_setspn_add,
+                       NET_TRANSPORT_ADS,
+                       N_("Add Service Principal Names (SPN)"),
+                       N_("net ads setspn add machine spn\n"
+                          "    Add Service Principal Names (SPN)")
+               },
+               {
+                       "delete",
+                       net_ads_setspn_delete,
+                       NET_TRANSPORT_ADS,
+                       N_("Delete Service Principal Names (SPN)"),
+                       N_("net ads setspn delete machine spn\n"
+                          "    Delete Service Principal Names (SPN)")
+               },
+               {NULL, NULL, 0, NULL, NULL}
+       };
+
+       return net_run_function(c, argc, argv, "net ads setspn", func);
+}
+
+static int net_ads_enctype_lookup_account(struct net_context *c,
+                                         ADS_STRUCT *ads,
+                                         const char *account,
+                                         LDAPMessage **res,
+                                         const char **enctype_str)
+{
+       const char *filter;
+       const char *attrs[] = {
+               "msDS-SupportedEncryptionTypes",
+               NULL
+       };
+       int count;
+       int ret = -1;
+       ADS_STATUS status;
+
+       filter = talloc_asprintf(c, "(&(objectclass=user)(sAMAccountName=%s))",
+                                account);
+       if (filter == NULL) {
+               goto done;
+       }
+
+       status = ads_search(ads, res, filter, attrs);
+       if (!ADS_ERR_OK(status)) {
+               d_printf(_("no account found with filter: %s\n"), filter);
+               goto done;
+       }
+
+       count = ads_count_replies(ads, *res);
+       switch (count) {
+       case 1:
+               break;
+       case 0:
+               d_printf(_("no account found with filter: %s\n"), filter);
+               goto done;
+       default:
+               d_printf(_("multiple accounts found with filter: %s\n"), filter);
+               goto done;
+       }
+
+       if (enctype_str) {
+               *enctype_str = ads_pull_string(ads, c, *res,
+                                              "msDS-SupportedEncryptionTypes");
+               if (*enctype_str == NULL) {
+                       d_printf(_("no msDS-SupportedEncryptionTypes attribute found\n"));
+                       goto done;
+               }
+       }
+
+       ret = 0;
+ done:
+       return ret;
+}
+
+static void net_ads_enctype_dump_enctypes(const char *username,
+                                         const char *enctype_str)
+{
+       int enctypes = atoi(enctype_str);
+
+       d_printf(_("'%s' uses \"msDS-SupportedEncryptionTypes\": %d (0x%08x)\n"),
+               username, enctypes, enctypes);
+
+       printf("[%s] 0x%08x DES-CBC-CRC\n",
+               enctypes & ENC_CRC32 ? "X" : " ",
+               ENC_CRC32);
+       printf("[%s] 0x%08x DES-CBC-MD5\n",
+               enctypes & ENC_RSA_MD5 ? "X" : " ",
+               ENC_RSA_MD5);
+       printf("[%s] 0x%08x RC4-HMAC\n",
+               enctypes & ENC_RC4_HMAC_MD5 ? "X" : " ",
+               ENC_RC4_HMAC_MD5);
+       printf("[%s] 0x%08x AES128-CTS-HMAC-SHA1-96\n",
+               enctypes & ENC_HMAC_SHA1_96_AES128 ? "X" : " ",
+               ENC_HMAC_SHA1_96_AES128);
+       printf("[%s] 0x%08x AES256-CTS-HMAC-SHA1-96\n",
+               enctypes & ENC_HMAC_SHA1_96_AES256 ? "X" : " ",
+               ENC_HMAC_SHA1_96_AES256);
+}
+
+static int net_ads_enctypes_list(struct net_context *c, int argc, const char **argv)
+{
+       int ret = -1;
+       ADS_STATUS status;
+       ADS_STRUCT *ads = NULL;
+       LDAPMessage *res = NULL;
+       const char *str = NULL;
+
+       if (c->display_usage || (argc < 1)) {
+               d_printf(  "%s\n"
+                          "net ads enctypes list\n"
+                          "    %s\n",
+                        _("Usage:"),
+                        _("List supported enctypes"));
+               return 0;
+       }
+
+       status = ads_startup(c, false, &ads);
+       if (!ADS_ERR_OK(status)) {
+               printf("startup failed\n");
+               return ret;
+       }
+
+       ret = net_ads_enctype_lookup_account(c, ads, argv[0], &res, &str);
+       if (ret) {
+               goto done;
+       }
+
+       net_ads_enctype_dump_enctypes(argv[0], str);
+
+       ret = 0;
+ done:
+       ads_msgfree(ads, res);
+       ads_destroy(&ads);
+
+       return ret;
+}
+
+static int net_ads_enctypes_set(struct net_context *c, int argc, const char **argv)
+{
+       int ret = -1;
+       ADS_STATUS status;
+       ADS_STRUCT *ads;
+       LDAPMessage *res = NULL;
+       const char *etype_list_str;
+       const char *dn;
+       ADS_MODLIST mods;
+       uint32_t etype_list;
+       const char *str;
+
+       if (c->display_usage || argc < 1) {
+               d_printf(  "%s\n"
+                          "net ads enctypes set <sAMAccountName> [enctypes]\n"
+                          "    %s\n",
+                        _("Usage:"),
+                        _("Set supported enctypes"));
+               return 0;
+       }
+
+       status = ads_startup(c, false, &ads);
+       if (!ADS_ERR_OK(status)) {
+               printf("startup failed\n");
+               return ret;
+       }
+
+       ret = net_ads_enctype_lookup_account(c, ads, argv[0], &res, NULL);
+       if (ret) {
+               goto done;
+       }
+
+       dn = ads_get_dn(ads, c, res);
+       if (dn == NULL) {
+               goto done;
+       }
+
+       etype_list = ENC_CRC32 | ENC_RSA_MD5 | ENC_RC4_HMAC_MD5;
+#ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
+       etype_list |= ENC_HMAC_SHA1_96_AES128;
+#endif
+#ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
+       etype_list |= ENC_HMAC_SHA1_96_AES256;
+#endif
+
+       if (argv[1] != NULL) {
+               sscanf(argv[1], "%i", &etype_list);
+       }
+
+       etype_list_str = talloc_asprintf(c, "%d", etype_list);
+       if (!etype_list_str) {
+               goto done;
+       }
+
+       mods = ads_init_mods(c);
+       if (!mods) {
+               goto done;
+       }
+
+       status = ads_mod_str(c, &mods, "msDS-SupportedEncryptionTypes",
+                            etype_list_str);
+       if (!ADS_ERR_OK(status)) {
+               goto done;
+       }
+
+       status = ads_gen_mod(ads, dn, mods);
+       if (!ADS_ERR_OK(status)) {
+               d_printf(_("failed to add msDS-SupportedEncryptionTypes: %s\n"),
+                       ads_errstr(status));
+               goto done;
+       }
+
+       ads_msgfree(ads, res);
+
+       ret = net_ads_enctype_lookup_account(c, ads, argv[0], &res, &str);
+       if (ret) {
+               goto done;
+       }
+
+       net_ads_enctype_dump_enctypes(argv[0], str);
+
+       ret = 0;
+ done:
+       ads_msgfree(ads, res);
+       ads_destroy(&ads);
+
+       return ret;
+}
+
+static int net_ads_enctypes_delete(struct net_context *c, int argc, const char **argv)
+{
+       int ret = -1;
+       ADS_STATUS status;
+       ADS_STRUCT *ads;
+       LDAPMessage *res = NULL;
+       const char *dn;
+       ADS_MODLIST mods;
+
+       if (c->display_usage || argc < 1) {
+               d_printf(  "%s\n"
+                          "net ads enctypes delete <sAMAccountName>\n"
+                          "    %s\n",
+                        _("Usage:"),
+                        _("Delete supported enctypes"));
+               return 0;
+       }
+
+       status = ads_startup(c, false, &ads);
+       if (!ADS_ERR_OK(status)) {
+               printf("startup failed\n");
+               return ret;
+       }
+
+       ret = net_ads_enctype_lookup_account(c, ads, argv[0], &res, NULL);
+       if (ret) {
+               goto done;
+       }
+
+       dn = ads_get_dn(ads, c, res);
+       if (dn == NULL) {
+               goto done;
+       }
+
+       mods = ads_init_mods(c);
+       if (!mods) {
+               goto done;
+       }
+
+       status = ads_mod_str(c, &mods, "msDS-SupportedEncryptionTypes", NULL);
+       if (!ADS_ERR_OK(status)) {
+               goto done;
+       }
+
+       status = ads_gen_mod(ads, dn, mods);
+       if (!ADS_ERR_OK(status)) {
+               d_printf(_("failed to remove msDS-SupportedEncryptionTypes: %s\n"),
+                       ads_errstr(status));
+               goto done;
+       }
+
+       ret = 0;
+
+ done:
+       ads_msgfree(ads, res);
+       ads_destroy(&ads);
+       return ret;
+}
+
+static int net_ads_enctypes(struct net_context *c, int argc, const char **argv)
+{
+       struct functable func[] = {
+               {
+                       "list",
+                       net_ads_enctypes_list,
+                       NET_TRANSPORT_ADS,
+                       N_("List the supported encryption types"),
+                       N_("net ads enctypes list\n"
+                          "    List the supported encryption types")
+               },
+               {
+                       "set",
+                       net_ads_enctypes_set,
+                       NET_TRANSPORT_ADS,
+                       N_("Set the supported encryption types"),
+                       N_("net ads enctypes set\n"
+                          "    Set the supported encryption types")
+               },
+               {
+                       "delete",
+                       net_ads_enctypes_delete,
+                       NET_TRANSPORT_ADS,
+                       N_("Delete the supported encryption types"),
+                       N_("net ads enctypes delete\n"
+                          "    Delete the supported encryption types")
+               },
+
+               {NULL, NULL, 0, NULL, NULL}
+       };
+
+       return net_run_function(c, argc, argv, "net ads enctypes", func);
+}
+
+
 int net_ads(struct net_context *c, int argc, const char **argv)
 {
        struct functable func[] = {
@@ -2826,7 +3557,7 @@ int net_ads(struct net_context *c, int argc, const char **argv)
                        "lookup",
                        net_ads_lookup,
                        NET_TRANSPORT_ADS,
-                       N_("Perfom CLDAP query on DC"),
+                       N_("Perform CLDAP query on DC"),
                        N_("net ads lookup\n"
                           "    Find the ADS DC using CLDAP lookups")
                },
@@ -2838,6 +3569,14 @@ int net_ads(struct net_context *c, int argc, const char **argv)
                        N_("net ads keytab\n"
                           "    Manage local keytab file")
                },
+               {
+                       "setspn",
+                       net_ads_setspn,
+                       NET_TRANSPORT_ADS,
+                       N_("Manage Service Principal Names (SPN)s"),
+                       N_("net ads spnset\n"
+                          "    Manage Service Principal Names (SPN)s")
+               },
                {
                        "gpo",
                        net_ads_gpo,
@@ -2854,6 +3593,14 @@ int net_ads(struct net_context *c, int argc, const char **argv)
                        N_("net ads kerberos\n"
                           "    Manage kerberos keytab")
                },
+               {
+                       "enctypes",
+                       net_ads_enctypes,
+                       NET_TRANSPORT_ADS,
+                       N_("List/modify supported encryption types"),
+                       N_("net ads enctypes\n"
+                          "    List/modify enctypes")
+               },
                {NULL, NULL, 0, NULL, NULL}
        };
 
@@ -2878,6 +3625,11 @@ int net_ads_kerberos(struct net_context *c, int argc, const char **argv)
        return net_ads_noads();
 }
 
+int net_ads_setspn(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();