Use libnet_Unjoin() for "net ads leave".
[ab/samba.git/.git] / source3 / utils / net_ads.c
1 /*
2    Samba Unix/Linux SMB client library
3    net ads commands
4    Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
5    Copyright (C) 2001 Remus Koos (remuskoos@yahoo.com)
6    Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
7    Copyright (C) 2006 Gerald (Jerry) Carter (jerry@samba.org)
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "utils/net.h"
25
26 #include "libnet/libnet.h"
27
28 #ifdef HAVE_ADS
29
30 int net_ads_usage(int argc, const char **argv)
31 {
32         d_printf("join [createupn[=principal]] [createcomputer=<org_unit>]\n");
33         d_printf("    Join the local machine to a ADS realm\n");
34         d_printf("leave\n");
35         d_printf("    Remove the local machine from a ADS realm\n");
36         d_printf("testjoin\n");
37         d_printf("    Validates the machine account in the domain\n");
38         d_printf("user\n");
39         d_printf("    List, add, or delete users in the realm\n");
40         d_printf("group\n");
41         d_printf("    List, add, or delete groups in the realm\n");
42         d_printf("info\n");
43         d_printf("    Displays details regarding a specific AD server\n");
44         d_printf("status\n");
45         d_printf("    Display details regarding the machine's account in AD\n");
46         d_printf("lookup\n");
47         d_printf("    Performs CLDAP query of AD domain controllers\n");
48         d_printf("password <username@realm> <password> -Uadmin_username@realm%%admin_pass\n");
49         d_printf("    Change a user's password using an admin account\n");
50         d_printf("    (note: use realm in UPPERCASE, prompts if password is obmitted)\n");
51         d_printf("changetrustpw\n");
52         d_printf("    Change the trust account password of this machine in the AD tree\n");
53         d_printf("printer [info | publish | remove] <printername> <servername>\n");
54         d_printf("    Lookup, add, or remove directory entry for a printer\n");
55         d_printf("{search,dn,sid}\n");
56         d_printf("    Issue LDAP search queries using a general filter, by DN, or by SID\n");
57         d_printf("keytab\n");
58         d_printf("    Manage a local keytab file based on the machine account in AD\n");
59         d_printf("dns\n");
60         d_printf("    Issue a dynamic DNS update request the server's hostname\n");
61         d_printf("    (using the machine credentials)\n");
62
63         return -1;
64 }
65
66 /* when we do not have sufficient input parameters to contact a remote domain
67  * we always fall back to our own realm - Guenther*/
68
69 static const char *assume_own_realm(void)
70 {
71         if (!opt_host && strequal(lp_workgroup(), opt_target_workgroup)) {
72                 return lp_realm();
73         }
74
75         return NULL;
76 }
77
78 /*
79   do a cldap netlogon query
80 */
81 static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
82 {
83         char addr[INET6_ADDRSTRLEN];
84         struct cldap_netlogon_reply reply;
85         struct GUID tmp_guid;
86
87         print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
88         if ( !ads_cldap_netlogon(addr, ads->server.realm, &reply ) ) {
89                 d_fprintf(stderr, "CLDAP query failed!\n");
90                 return -1;
91         }
92
93         d_printf("Information for Domain Controller: %s\n\n",
94                 addr);
95
96         d_printf("Response Type: ");
97         switch (reply.type) {
98         case SAMLOGON_AD_UNK_R:
99                 d_printf("SAMLOGON\n");
100                 break;
101         case SAMLOGON_AD_R:
102                 d_printf("SAMLOGON_USER\n");
103                 break;
104         default:
105                 d_printf("0x%x\n", reply.type);
106                 break;
107         }
108
109         smb_uuid_unpack(reply.guid, &tmp_guid);
110         d_printf("GUID: %s\n", smb_uuid_string(talloc_tos(), tmp_guid));
111
112         d_printf("Flags:\n"
113                  "\tIs a PDC:                                   %s\n"
114                  "\tIs a GC of the forest:                      %s\n"
115                  "\tIs an LDAP server:                          %s\n"
116                  "\tSupports DS:                                %s\n"
117                  "\tIs running a KDC:                           %s\n"
118                  "\tIs running time services:                   %s\n"
119                  "\tIs the closest DC:                          %s\n"
120                  "\tIs writable:                                %s\n"
121                  "\tHas a hardware clock:                       %s\n"
122                  "\tIs a non-domain NC serviced by LDAP server: %s\n",
123                  (reply.flags & ADS_PDC) ? "yes" : "no",
124                  (reply.flags & ADS_GC) ? "yes" : "no",
125                  (reply.flags & ADS_LDAP) ? "yes" : "no",
126                  (reply.flags & ADS_DS) ? "yes" : "no",
127                  (reply.flags & ADS_KDC) ? "yes" : "no",
128                  (reply.flags & ADS_TIMESERV) ? "yes" : "no",
129                  (reply.flags & ADS_CLOSEST) ? "yes" : "no",
130                  (reply.flags & ADS_WRITABLE) ? "yes" : "no",
131                  (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
132                  (reply.flags & ADS_NDNC) ? "yes" : "no");
133
134         printf("Forest:\t\t\t%s\n", reply.forest);
135         printf("Domain:\t\t\t%s\n", reply.domain);
136         printf("Domain Controller:\t%s\n", reply.hostname);
137
138         printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
139         printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
140
141         if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
142         if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
143
144         printf("Server Site Name :\t\t%s\n", reply.server_site_name);
145         printf("Client Site Name :\t\t%s\n", reply.client_site_name);
146
147         d_printf("NT Version: %d\n", reply.version);
148         d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
149         d_printf("LM20 Token: %.2x\n", reply.lm20_token);
150
151         return 0;
152 }
153
154 /*
155   this implements the CLDAP based netlogon lookup requests
156   for finding the domain controller of a ADS domain
157 */
158 static int net_ads_lookup(int argc, const char **argv)
159 {
160         ADS_STRUCT *ads;
161
162         if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
163                 d_fprintf(stderr, "Didn't find the cldap server!\n");
164                 return -1;
165         }
166
167         if (!ads->config.realm) {
168                 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
169                 ads->ldap.port = 389;
170         }
171
172         return net_ads_cldap_netlogon(ads);
173 }
174
175
176
177 static int net_ads_info(int argc, const char **argv)
178 {
179         ADS_STRUCT *ads;
180         char addr[INET6_ADDRSTRLEN];
181
182         if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
183                 d_fprintf(stderr, "Didn't find the ldap server!\n");
184                 return -1;
185         }
186
187         if (!ads || !ads->config.realm) {
188                 d_fprintf(stderr, "Didn't find the ldap server!\n");
189                 return -1;
190         }
191
192         /* Try to set the server's current time since we didn't do a full
193            TCP LDAP session initially */
194
195         if ( !ADS_ERR_OK(ads_current_time( ads )) ) {
196                 d_fprintf( stderr, "Failed to get server's current time!\n");
197         }
198
199         print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
200
201         d_printf("LDAP server: %s\n", addr);
202         d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
203         d_printf("Realm: %s\n", ads->config.realm);
204         d_printf("Bind Path: %s\n", ads->config.bind_path);
205         d_printf("LDAP port: %d\n", ads->ldap.port);
206         d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
207
208         d_printf("KDC server: %s\n", ads->auth.kdc_server );
209         d_printf("Server time offset: %d\n", ads->auth.time_offset );
210
211         return 0;
212 }
213
214 static void use_in_memory_ccache(void) {
215         /* Use in-memory credentials cache so we do not interfere with
216          * existing credentials */
217         setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
218 }
219
220 static ADS_STATUS ads_startup_int(bool only_own_domain, uint32 auth_flags, ADS_STRUCT **ads_ret)
221 {
222         ADS_STRUCT *ads = NULL;
223         ADS_STATUS status;
224         bool need_password = False;
225         bool second_time = False;
226         char *cp;
227         const char *realm = NULL;
228         bool tried_closest_dc = False;
229
230         /* lp_realm() should be handled by a command line param,
231            However, the join requires that realm be set in smb.conf
232            and compares our realm with the remote server's so this is
233            ok until someone needs more flexibility */
234
235         *ads_ret = NULL;
236
237 retry_connect:
238         if (only_own_domain) {
239                 realm = lp_realm();
240         } else {
241                 realm = assume_own_realm();
242         }
243
244         ads = ads_init(realm, opt_target_workgroup, opt_host);
245
246         if (!opt_user_name) {
247                 opt_user_name = "administrator";
248         }
249
250         if (opt_user_specified) {
251                 need_password = True;
252         }
253
254 retry:
255         if (!opt_password && need_password && !opt_machine_pass) {
256                 opt_password = net_prompt_pass(opt_user_name);
257                 if (!opt_password) {
258                         ads_destroy(&ads);
259                         return ADS_ERROR(LDAP_NO_MEMORY);
260                 }
261         }
262
263         if (opt_password) {
264                 use_in_memory_ccache();
265                 SAFE_FREE(ads->auth.password);
266                 ads->auth.password = smb_xstrdup(opt_password);
267         }
268
269         ads->auth.flags |= auth_flags;
270         SAFE_FREE(ads->auth.user_name);
271         ads->auth.user_name = smb_xstrdup(opt_user_name);
272
273        /*
274         * If the username is of the form "name@realm",
275         * extract the realm and convert to upper case.
276         * This is only used to establish the connection.
277         */
278        if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
279                 *cp++ = '\0';
280                 SAFE_FREE(ads->auth.realm);
281                 ads->auth.realm = smb_xstrdup(cp);
282                 strupper_m(ads->auth.realm);
283        }
284
285         status = ads_connect(ads);
286
287         if (!ADS_ERR_OK(status)) {
288
289                 if (NT_STATUS_EQUAL(ads_ntstatus(status),
290                                     NT_STATUS_NO_LOGON_SERVERS)) {
291                         DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
292                         ads_destroy(&ads);
293                         return status;
294                 }
295
296                 if (!need_password && !second_time && !(auth_flags & ADS_AUTH_NO_BIND)) {
297                         need_password = True;
298                         second_time = True;
299                         goto retry;
300                 } else {
301                         ads_destroy(&ads);
302                         return status;
303                 }
304         }
305
306         /* when contacting our own domain, make sure we use the closest DC.
307          * This is done by reconnecting to ADS because only the first call to
308          * ads_connect will give us our own sitename */
309
310         if ((only_own_domain || !opt_host) && !tried_closest_dc) {
311
312                 tried_closest_dc = True; /* avoid loop */
313
314                 if (!ads->config.tried_closest_dc) {
315
316                         namecache_delete(ads->server.realm, 0x1C);
317                         namecache_delete(ads->server.workgroup, 0x1C);
318
319                         ads_destroy(&ads);
320                         ads = NULL;
321
322                         goto retry_connect;
323                 }
324         }
325
326         *ads_ret = ads;
327         return status;
328 }
329
330 ADS_STATUS ads_startup(bool only_own_domain, ADS_STRUCT **ads)
331 {
332         return ads_startup_int(only_own_domain, 0, ads);
333 }
334
335 ADS_STATUS ads_startup_nobind(bool only_own_domain, ADS_STRUCT **ads)
336 {
337         return ads_startup_int(only_own_domain, ADS_AUTH_NO_BIND, ads);
338 }
339
340 /*
341   Check to see if connection can be made via ads.
342   ads_startup() stores the password in opt_password if it needs to so
343   that rpc or rap can use it without re-prompting.
344 */
345 static int net_ads_check_int(const char *realm, const char *workgroup, const char *host)
346 {
347         ADS_STRUCT *ads;
348         ADS_STATUS status;
349
350         if ( (ads = ads_init( realm, workgroup, host )) == NULL ) {
351                 return -1;
352         }
353
354         ads->auth.flags |= ADS_AUTH_NO_BIND;
355
356         status = ads_connect(ads);
357         if ( !ADS_ERR_OK(status) ) {
358                 return -1;
359         }
360
361         ads_destroy(&ads);
362         return 0;
363 }
364
365 int net_ads_check_our_domain(void)
366 {
367         return net_ads_check_int(lp_realm(), lp_workgroup(), NULL);
368 }
369
370 int net_ads_check(void)
371 {
372         return net_ads_check_int(NULL, opt_workgroup, opt_host);
373 }
374
375 /*
376    determine the netbios workgroup name for a domain
377  */
378 static int net_ads_workgroup(int argc, const char **argv)
379 {
380         ADS_STRUCT *ads;
381         char addr[INET6_ADDRSTRLEN];
382         struct cldap_netlogon_reply reply;
383
384         if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
385                 d_fprintf(stderr, "Didn't find the cldap server!\n");
386                 return -1;
387         }
388
389         if (!ads->config.realm) {
390                 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
391                 ads->ldap.port = 389;
392         }
393
394         print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
395         if ( !ads_cldap_netlogon(addr, ads->server.realm, &reply ) ) {
396                 d_fprintf(stderr, "CLDAP query failed!\n");
397                 return -1;
398         }
399
400         d_printf("Workgroup: %s\n", reply.netbios_domain);
401
402         ads_destroy(&ads);
403
404         return 0;
405 }
406
407
408
409 static bool usergrp_display(ADS_STRUCT *ads, char *field, void **values, void *data_area)
410 {
411         char **disp_fields = (char **) data_area;
412
413         if (!field) { /* must be end of record */
414                 if (disp_fields[0]) {
415                         if (!strchr_m(disp_fields[0], '$')) {
416                                 if (disp_fields[1])
417                                         d_printf("%-21.21s %s\n",
418                                                disp_fields[0], disp_fields[1]);
419                                 else
420                                         d_printf("%s\n", disp_fields[0]);
421                         }
422                 }
423                 SAFE_FREE(disp_fields[0]);
424                 SAFE_FREE(disp_fields[1]);
425                 return True;
426         }
427         if (!values) /* must be new field, indicate string field */
428                 return True;
429         if (StrCaseCmp(field, "sAMAccountName") == 0) {
430                 disp_fields[0] = SMB_STRDUP((char *) values[0]);
431         }
432         if (StrCaseCmp(field, "description") == 0)
433                 disp_fields[1] = SMB_STRDUP((char *) values[0]);
434         return True;
435 }
436
437 static int net_ads_user_usage(int argc, const char **argv)
438 {
439         return net_help_user(argc, argv);
440 }
441
442 static int ads_user_add(int argc, const char **argv)
443 {
444         ADS_STRUCT *ads;
445         ADS_STATUS status;
446         char *upn, *userdn;
447         LDAPMessage *res=NULL;
448         int rc = -1;
449         char *ou_str = NULL;
450
451         if (argc < 1) return net_ads_user_usage(argc, argv);
452
453         if (!ADS_ERR_OK(ads_startup(False, &ads))) {
454                 return -1;
455         }
456
457         status = ads_find_user_acct(ads, &res, argv[0]);
458
459         if (!ADS_ERR_OK(status)) {
460                 d_fprintf(stderr, "ads_user_add: %s\n", ads_errstr(status));
461                 goto done;
462         }
463
464         if (ads_count_replies(ads, res)) {
465                 d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
466                 goto done;
467         }
468
469         if (opt_container) {
470                 ou_str = SMB_STRDUP(opt_container);
471         } else {
472                 ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
473         }
474
475         status = ads_add_user_acct(ads, argv[0], ou_str, opt_comment);
476
477         if (!ADS_ERR_OK(status)) {
478                 d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
479                          ads_errstr(status));
480                 goto done;
481         }
482
483         /* if no password is to be set, we're done */
484         if (argc == 1) {
485                 d_printf("User %s added\n", argv[0]);
486                 rc = 0;
487                 goto done;
488         }
489
490         /* try setting the password */
491         asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
492         status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
493                                        ads->auth.time_offset);
494         safe_free(upn);
495         if (ADS_ERR_OK(status)) {
496                 d_printf("User %s added\n", argv[0]);
497                 rc = 0;
498                 goto done;
499         }
500
501         /* password didn't set, delete account */
502         d_fprintf(stderr, "Could not add user %s.  Error setting password %s\n",
503                  argv[0], ads_errstr(status));
504         ads_msgfree(ads, res);
505         status=ads_find_user_acct(ads, &res, argv[0]);
506         if (ADS_ERR_OK(status)) {
507                 userdn = ads_get_dn(ads, res);
508                 ads_del_dn(ads, userdn);
509                 ads_memfree(ads, userdn);
510         }
511
512  done:
513         if (res)
514                 ads_msgfree(ads, res);
515         ads_destroy(&ads);
516         SAFE_FREE(ou_str);
517         return rc;
518 }
519
520 static int ads_user_info(int argc, const char **argv)
521 {
522         ADS_STRUCT *ads;
523         ADS_STATUS rc;
524         LDAPMessage *res;
525         const char *attrs[] = {"memberOf", NULL};
526         char *searchstring=NULL;
527         char **grouplist;
528         char *escaped_user;
529
530         if (argc < 1) {
531                 return net_ads_user_usage(argc, argv);
532         }
533
534         escaped_user = escape_ldap_string_alloc(argv[0]);
535
536         if (!escaped_user) {
537                 d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
538                 return -1;
539         }
540
541         if (!ADS_ERR_OK(ads_startup(False, &ads))) {
542                 SAFE_FREE(escaped_user);
543                 return -1;
544         }
545
546         asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
547         rc = ads_search(ads, &res, searchstring, attrs);
548         safe_free(searchstring);
549
550         if (!ADS_ERR_OK(rc)) {
551                 d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
552                 ads_destroy(&ads);
553                 SAFE_FREE(escaped_user);
554                 return -1;
555         }
556
557         grouplist = ldap_get_values((LDAP *)ads->ldap.ld,
558                                     (LDAPMessage *)res, "memberOf");
559
560         if (grouplist) {
561                 int i;
562                 char **groupname;
563                 for (i=0;grouplist[i];i++) {
564                         groupname = ldap_explode_dn(grouplist[i], 1);
565                         d_printf("%s\n", groupname[0]);
566                         ldap_value_free(groupname);
567                 }
568                 ldap_value_free(grouplist);
569         }
570
571         ads_msgfree(ads, res);
572         ads_destroy(&ads);
573         SAFE_FREE(escaped_user);
574         return 0;
575 }
576
577 static int ads_user_delete(int argc, const char **argv)
578 {
579         ADS_STRUCT *ads;
580         ADS_STATUS rc;
581         LDAPMessage *res = NULL;
582         char *userdn;
583
584         if (argc < 1) {
585                 return net_ads_user_usage(argc, argv);
586         }
587
588         if (!ADS_ERR_OK(ads_startup(False, &ads))) {
589                 return -1;
590         }
591
592         rc = ads_find_user_acct(ads, &res, argv[0]);
593         if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
594                 d_printf("User %s does not exist.\n", argv[0]);
595                 ads_msgfree(ads, res);
596                 ads_destroy(&ads);
597                 return -1;
598         }
599         userdn = ads_get_dn(ads, res);
600         ads_msgfree(ads, res);
601         rc = ads_del_dn(ads, userdn);
602         ads_memfree(ads, userdn);
603         if (ADS_ERR_OK(rc)) {
604                 d_printf("User %s deleted\n", argv[0]);
605                 ads_destroy(&ads);
606                 return 0;
607         }
608         d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0],
609                  ads_errstr(rc));
610         ads_destroy(&ads);
611         return -1;
612 }
613
614 int net_ads_user(int argc, const char **argv)
615 {
616         struct functable func[] = {
617                 {"ADD", ads_user_add},
618                 {"INFO", ads_user_info},
619                 {"DELETE", ads_user_delete},
620                 {NULL, NULL}
621         };
622         ADS_STRUCT *ads;
623         ADS_STATUS rc;
624         const char *shortattrs[] = {"sAMAccountName", NULL};
625         const char *longattrs[] = {"sAMAccountName", "description", NULL};
626         char *disp_fields[2] = {NULL, NULL};
627
628         if (argc == 0) {
629                 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
630                         return -1;
631                 }
632
633                 if (opt_long_list_entries)
634                         d_printf("\nUser name             Comment"\
635                                  "\n-----------------------------\n");
636
637                 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
638                                           LDAP_SCOPE_SUBTREE,
639                                           "(objectCategory=user)",
640                                           opt_long_list_entries ? longattrs :
641                                           shortattrs, usergrp_display,
642                                           disp_fields);
643                 ads_destroy(&ads);
644                 return ADS_ERR_OK(rc) ? 0 : -1;
645         }
646
647         return net_run_function(argc, argv, func, net_ads_user_usage);
648 }
649
650 static int net_ads_group_usage(int argc, const char **argv)
651 {
652         return net_help_group(argc, argv);
653 }
654
655 static int ads_group_add(int argc, const char **argv)
656 {
657         ADS_STRUCT *ads;
658         ADS_STATUS status;
659         LDAPMessage *res=NULL;
660         int rc = -1;
661         char *ou_str = NULL;
662
663         if (argc < 1) {
664                 return net_ads_group_usage(argc, argv);
665         }
666
667         if (!ADS_ERR_OK(ads_startup(False, &ads))) {
668                 return -1;
669         }
670
671         status = ads_find_user_acct(ads, &res, argv[0]);
672
673         if (!ADS_ERR_OK(status)) {
674                 d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
675                 goto done;
676         }
677
678         if (ads_count_replies(ads, res)) {
679                 d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
680                 goto done;
681         }
682
683         if (opt_container) {
684                 ou_str = SMB_STRDUP(opt_container);
685         } else {
686                 ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
687         }
688
689         status = ads_add_group_acct(ads, argv[0], ou_str, opt_comment);
690
691         if (ADS_ERR_OK(status)) {
692                 d_printf("Group %s added\n", argv[0]);
693                 rc = 0;
694         } else {
695                 d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
696                          ads_errstr(status));
697         }
698
699  done:
700         if (res)
701                 ads_msgfree(ads, res);
702         ads_destroy(&ads);
703         SAFE_FREE(ou_str);
704         return rc;
705 }
706
707 static int ads_group_delete(int argc, const char **argv)
708 {
709         ADS_STRUCT *ads;
710         ADS_STATUS rc;
711         LDAPMessage *res = NULL;
712         char *groupdn;
713
714         if (argc < 1) {
715                 return net_ads_group_usage(argc, argv);
716         }
717
718         if (!ADS_ERR_OK(ads_startup(False, &ads))) {
719                 return -1;
720         }
721
722         rc = ads_find_user_acct(ads, &res, argv[0]);
723         if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
724                 d_printf("Group %s does not exist.\n", argv[0]);
725                 ads_msgfree(ads, res);
726                 ads_destroy(&ads);
727                 return -1;
728         }
729         groupdn = ads_get_dn(ads, res);
730         ads_msgfree(ads, res);
731         rc = ads_del_dn(ads, groupdn);
732         ads_memfree(ads, groupdn);
733         if (ADS_ERR_OK(rc)) {
734                 d_printf("Group %s deleted\n", argv[0]);
735                 ads_destroy(&ads);
736                 return 0;
737         }
738         d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0],
739                  ads_errstr(rc));
740         ads_destroy(&ads);
741         return -1;
742 }
743
744 int net_ads_group(int argc, const char **argv)
745 {
746         struct functable func[] = {
747                 {"ADD", ads_group_add},
748                 {"DELETE", ads_group_delete},
749                 {NULL, NULL}
750         };
751         ADS_STRUCT *ads;
752         ADS_STATUS rc;
753         const char *shortattrs[] = {"sAMAccountName", NULL};
754         const char *longattrs[] = {"sAMAccountName", "description", NULL};
755         char *disp_fields[2] = {NULL, NULL};
756
757         if (argc == 0) {
758                 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
759                         return -1;
760                 }
761
762                 if (opt_long_list_entries)
763                         d_printf("\nGroup name            Comment"\
764                                  "\n-----------------------------\n");
765                 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
766                                           LDAP_SCOPE_SUBTREE,
767                                           "(objectCategory=group)",
768                                           opt_long_list_entries ? longattrs :
769                                           shortattrs, usergrp_display,
770                                           disp_fields);
771
772                 ads_destroy(&ads);
773                 return ADS_ERR_OK(rc) ? 0 : -1;
774         }
775         return net_run_function(argc, argv, func, net_ads_group_usage);
776 }
777
778 static int net_ads_status(int argc, const char **argv)
779 {
780         ADS_STRUCT *ads;
781         ADS_STATUS rc;
782         LDAPMessage *res;
783
784         if (!ADS_ERR_OK(ads_startup(True, &ads))) {
785                 return -1;
786         }
787
788         rc = ads_find_machine_acct(ads, &res, global_myname());
789         if (!ADS_ERR_OK(rc)) {
790                 d_fprintf(stderr, "ads_find_machine_acct: %s\n", ads_errstr(rc));
791                 ads_destroy(&ads);
792                 return -1;
793         }
794
795         if (ads_count_replies(ads, res) == 0) {
796                 d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
797                 ads_destroy(&ads);
798                 return -1;
799         }
800
801         ads_dump(ads, res);
802         ads_destroy(&ads);
803         return 0;
804 }
805
806 /*******************************************************************
807  Leave an AD domain.  Windows XP disables the machine account.
808  We'll try the same.  The old code would do an LDAP delete.
809  That only worked using the machine creds because added the machine
810  with full control to the computer object's ACL.
811 *******************************************************************/
812
813 static int net_ads_leave(int argc, const char **argv)
814 {
815         TALLOC_CTX *ctx;
816         struct libnet_UnjoinCtx *r = NULL;
817         WERROR werr;
818
819         if (!(ctx = talloc_init("net_ads_leave"))) {
820                 d_fprintf(stderr, "Could not initialise talloc context.\n");
821                 return -1;
822         }
823
824         use_in_memory_ccache();
825
826         werr = libnet_init_UnjoinCtx(ctx, &r);
827         if (!W_ERROR_IS_OK(werr)) {
828                 d_fprintf(stderr, "Could not initialise unjoin context.\n");
829                 return -1;
830         }
831
832         r->in.debug             = opt_verbose;
833         r->in.dc_name           = opt_host;
834         r->in.domain_name       = lp_realm();
835         r->in.admin_account     = opt_user_name;
836         r->in.admin_password    = net_prompt_pass(opt_user_name);
837         r->in.unjoin_flags      = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
838                                   WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE;
839
840         werr = libnet_Unjoin(ctx, r);
841         if (!W_ERROR_IS_OK(werr)) {
842                 d_printf("%s: %s\n", get_friendly_werror_msg(werr),
843                          r->out.error_string ? r->out.error_string : "");
844                 goto done;
845         }
846
847         if (W_ERROR_IS_OK(werr)) {
848                 d_printf("Deleted account for '%s' in realm '%s'\n",
849                         r->in.machine_name, r->out.dns_domain_name);
850                 goto done;
851         }
852
853         /* We couldn't delete it - see if the disable succeeded. */
854         if (r->out.disabled_machine_account) {
855                 d_printf("Disabled account for '%s' in realm '%s'\n",
856                         r->in.machine_name, r->out.dns_domain_name);
857                 werr = WERR_OK;
858                 goto done;
859         }
860
861         d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
862                   r->in.machine_name, r->out.dns_domain_name);
863
864  done:
865         TALLOC_FREE(r);
866         TALLOC_FREE(ctx);
867
868         if (W_ERROR_IS_OK(werr)) {
869                 return 0;
870         }
871
872         return -1;
873 }
874
875 static NTSTATUS net_ads_join_ok(void)
876 {
877         ADS_STRUCT *ads = NULL;
878         ADS_STATUS status;
879
880         if (!secrets_init()) {
881                 DEBUG(1,("Failed to initialise secrets database\n"));
882                 return NT_STATUS_ACCESS_DENIED;
883         }
884
885         net_use_krb_machine_account();
886
887         status = ads_startup(True, &ads);
888         if (!ADS_ERR_OK(status)) {
889                 return ads_ntstatus(status);
890         }
891
892         ads_destroy(&ads);
893         return NT_STATUS_OK;
894 }
895
896 /*
897   check that an existing join is OK
898  */
899 int net_ads_testjoin(int argc, const char **argv)
900 {
901         NTSTATUS status;
902         use_in_memory_ccache();
903
904         /* Display success or failure */
905         status = net_ads_join_ok();
906         if (!NT_STATUS_IS_OK(status)) {
907                 fprintf(stderr,"Join to domain is not valid: %s\n",
908                         get_friendly_nt_error_msg(status));
909                 return -1;
910         }
911
912         printf("Join is OK\n");
913         return 0;
914 }
915
916 /*******************************************************************
917   Simple configu checks before beginning the join
918  ********************************************************************/
919
920 static NTSTATUS check_ads_config( void )
921 {
922         if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
923                 d_printf("Host is not configured as a member server.\n");
924                 return NT_STATUS_INVALID_DOMAIN_ROLE;
925         }
926
927         if (strlen(global_myname()) > 15) {
928                 d_printf("Our netbios name can be at most 15 chars long, "
929                          "\"%s\" is %u chars long\n", global_myname(),
930                          (unsigned int)strlen(global_myname()));
931                 return NT_STATUS_NAME_TOO_LONG;
932         }
933
934         if ( lp_security() == SEC_ADS && !*lp_realm()) {
935                 d_fprintf(stderr, "realm must be set in in %s for ADS "
936                         "join to succeed.\n", get_dyn_CONFIGFILE());
937                 return NT_STATUS_INVALID_PARAMETER;
938         }
939
940         if (!secrets_init()) {
941                 DEBUG(1,("Failed to initialise secrets database\n"));
942                 /* This is a good bet for failure of secrets_init ... */
943                 return NT_STATUS_ACCESS_DENIED;
944         }
945
946         return NT_STATUS_OK;
947 }
948
949 /*******************************************************************
950  Do the domain join
951  ********************************************************************/
952
953 static NTSTATUS net_join_domain(TALLOC_CTX *ctx, const char *servername,
954                                 struct sockaddr_storage *pss,
955                                 const char **domain,
956                                 DOM_SID **dom_sid,
957                                 const char *password)
958 {
959         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
960         struct cli_state *cli = NULL;
961
962         ret = connect_to_ipc_krb5(&cli, pss, servername);
963         if ( !NT_STATUS_IS_OK(ret) ) {
964                 goto done;
965         }
966
967         ret = netdom_get_domain_sid( ctx, cli, domain, dom_sid );
968         if ( !NT_STATUS_IS_OK(ret) ) {
969                 goto done;
970         }
971
972         /* cli->server_domain is not filled in when using krb5
973            session setups */
974
975         saf_store( *domain, cli->desthost );
976
977         ret = netdom_join_domain( ctx, cli, *dom_sid, password, ND_TYPE_AD );
978
979 done:
980         if ( cli )
981                 cli_shutdown(cli);
982
983         return ret;
984 }
985
986 /*******************************************************************
987  Set a machines dNSHostName and servicePrincipalName attributes
988  ********************************************************************/
989
990 static ADS_STATUS net_set_machine_spn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s )
991 {
992         ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
993         char *new_dn;
994         ADS_MODLIST mods;
995         const char *servicePrincipalName[3] = {NULL, NULL, NULL};
996         char *psp;
997         fstring my_fqdn;
998         LDAPMessage *res = NULL;
999         char *dn_string = NULL;
1000         const char *machine_name = global_myname();
1001         int count;
1002
1003         if ( !machine_name ) {
1004                 return ADS_ERROR(LDAP_NO_MEMORY);
1005         }
1006
1007         /* Find our DN */
1008
1009         status = ads_find_machine_acct(ads_s, &res, machine_name);
1010         if (!ADS_ERR_OK(status))
1011                 return status;
1012
1013         if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1014                 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1015                 return ADS_ERROR(LDAP_NO_MEMORY);
1016         }
1017
1018         if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1019                 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1020                 goto done;
1021         }
1022
1023         new_dn = talloc_strdup(ctx, dn_string);
1024         ads_memfree(ads_s, dn_string);
1025         if (!new_dn) {
1026                 return ADS_ERROR(LDAP_NO_MEMORY);
1027         }
1028
1029         /* Windows only creates HOST/shortname & HOST/fqdn. */
1030
1031         if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) )
1032                 goto done;
1033         strupper_m(psp);
1034         servicePrincipalName[0] = psp;
1035
1036         name_to_fqdn(my_fqdn, machine_name);
1037         strlower_m(my_fqdn);
1038         if ( !(psp = talloc_asprintf(ctx, "HOST/%s", my_fqdn)) )
1039                 goto done;
1040         servicePrincipalName[1] = psp;
1041
1042         if (!(mods = ads_init_mods(ctx))) {
1043                 goto done;
1044         }
1045
1046         /* fields of primary importance */
1047
1048         ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1049         ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1050
1051         status = ads_gen_mod(ads_s, new_dn, mods);
1052
1053 done:
1054         ads_msgfree(ads_s, res);
1055
1056         return status;
1057 }
1058
1059 /*******************************************************************
1060  Set a machines dNSHostName and servicePrincipalName attributes
1061  ********************************************************************/
1062
1063 static ADS_STATUS net_set_machine_upn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s, const char *upn )
1064 {
1065         ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
1066         char *new_dn;
1067         ADS_MODLIST mods;
1068         LDAPMessage *res = NULL;
1069         char *dn_string = NULL;
1070         const char *machine_name = global_myname();
1071         int count;
1072
1073         if ( !machine_name ) {
1074                 return ADS_ERROR(LDAP_NO_MEMORY);
1075         }
1076
1077         /* Find our DN */
1078
1079         status = ads_find_machine_acct(ads_s, &res, machine_name);
1080         if (!ADS_ERR_OK(status))
1081                 return status;
1082
1083         if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1084                 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1085                 return ADS_ERROR(LDAP_NO_MEMORY);
1086         }
1087
1088         if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1089                 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1090                 goto done;
1091         }
1092
1093         new_dn = talloc_strdup(ctx, dn_string);
1094         ads_memfree(ads_s, dn_string);
1095         if (!new_dn) {
1096                 return ADS_ERROR(LDAP_NO_MEMORY);
1097         }
1098
1099         /* now do the mods */
1100
1101         if (!(mods = ads_init_mods(ctx))) {
1102                 goto done;
1103         }
1104
1105         /* fields of primary importance */
1106
1107         ads_mod_str(ctx, &mods, "userPrincipalName", upn);
1108
1109         status = ads_gen_mod(ads_s, new_dn, mods);
1110
1111 done:
1112         ads_msgfree(ads_s, res);
1113
1114         return status;
1115 }
1116
1117 /*******************************************************************
1118  Set a machines dNSHostName and servicePrincipalName attributes
1119  ********************************************************************/
1120
1121 static ADS_STATUS net_set_os_attributes(TALLOC_CTX *ctx, ADS_STRUCT *ads_s,
1122                                         const char *os_name, const char *os_version )
1123 {
1124         ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
1125         char *new_dn;
1126         ADS_MODLIST mods;
1127         LDAPMessage *res = NULL;
1128         char *dn_string = NULL;
1129         const char *machine_name = global_myname();
1130         int count;
1131         char *os_sp = NULL;
1132
1133         if ( !os_name || !os_version ) {
1134                 return ADS_ERROR(LDAP_NO_MEMORY);
1135         }
1136
1137         /* Find our DN */
1138
1139         status = ads_find_machine_acct(ads_s, &res, machine_name);
1140         if (!ADS_ERR_OK(status))
1141                 return status;
1142
1143         if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1144                 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1145                 return ADS_ERROR(LDAP_NO_MEMORY);
1146         }
1147
1148         if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1149                 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1150                 goto done;
1151         }
1152
1153         new_dn = talloc_strdup(ctx, dn_string);
1154         ads_memfree(ads_s, dn_string);
1155         if (!new_dn) {
1156                 return ADS_ERROR(LDAP_NO_MEMORY);
1157         }
1158
1159         /* now do the mods */
1160
1161         if (!(mods = ads_init_mods(ctx))) {
1162                 goto done;
1163         }
1164
1165         os_sp = talloc_asprintf( ctx, "Samba %s", SAMBA_VERSION_STRING );
1166
1167         /* fields of primary importance */
1168
1169         ads_mod_str(ctx, &mods, "operatingSystem", os_name);
1170         ads_mod_str(ctx, &mods, "operatingSystemVersion", os_version);
1171         if ( os_sp )
1172                 ads_mod_str(ctx, &mods, "operatingSystemServicePack", os_sp);
1173
1174         status = ads_gen_mod(ads_s, new_dn, mods);
1175
1176 done:
1177         ads_msgfree(ads_s, res);
1178         TALLOC_FREE( os_sp );
1179
1180         return status;
1181 }
1182
1183 /*******************************************************************
1184   join a domain using ADS (LDAP mods)
1185  ********************************************************************/
1186
1187 static ADS_STATUS net_precreate_machine_acct( ADS_STRUCT *ads, const char *ou )
1188 {
1189         ADS_STATUS rc = ADS_ERROR(LDAP_SERVER_DOWN);
1190         char *ou_str = NULL;
1191         char *dn = NULL;
1192         LDAPMessage *res = NULL;
1193         bool moved;
1194
1195         ou_str = ads_ou_string(ads, ou);
1196         if (asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path) == -1) {
1197                 rc = ADS_ERROR(LDAP_NO_MEMORY);
1198                 goto done;
1199         }
1200
1201         rc = ads_search_dn(ads, &res, dn, NULL);
1202         if (!ADS_ERR_OK(rc)) {
1203                 d_fprintf(stderr, "The specified OU does not exist.\n");
1204                 goto done;
1205         }
1206
1207                 /* Attempt to create the machine account and bail if this fails.
1208                    Assume that the admin wants exactly what they requested */
1209
1210                 rc = ads_create_machine_acct( ads, global_myname(), dn );
1211         if (ADS_ERR_OK(rc)) {
1212                 DEBUG(1, ("machine account created\n"));
1213                 goto done;
1214                 }
1215         if ( !(rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS) ) {
1216                 DEBUG(1, ("machine account creation failed\n"));
1217                 goto done;
1218         }
1219
1220         rc = ads_move_machine_acct(ads, global_myname(), dn, &moved);
1221         if (!ADS_ERR_OK(rc)) {
1222                 DEBUG(1, ("failure to locate/move pre-existing machine account\n"));
1223                 goto done;
1224         }
1225
1226         if (moved) {
1227                 d_printf("The machine account was moved into the specified OU.\n");
1228         } else {
1229                 d_printf("The machine account already exists in the specified OU.\n");
1230         }
1231
1232 done:
1233         ads_msgfree(ads, res);
1234         SAFE_FREE( ou_str );
1235         SAFE_FREE( dn );
1236
1237         return rc;
1238 }
1239
1240 /************************************************************************
1241  ************************************************************************/
1242
1243 static bool net_derive_salting_principal( TALLOC_CTX *ctx, ADS_STRUCT *ads )
1244 {
1245         uint32 domain_func;
1246         ADS_STATUS status;
1247         fstring salt;
1248         char *std_salt;
1249         const char *machine_name = global_myname();
1250
1251         status = ads_domain_func_level( ads, &domain_func );
1252         if ( !ADS_ERR_OK(status) ) {
1253                 DEBUG(2,("Failed to determine domain functional level!\n"));
1254                 return False;
1255         }
1256
1257         /* go ahead and setup the default salt */
1258
1259         if ( (std_salt = kerberos_standard_des_salt()) == NULL ) {
1260                 d_fprintf(stderr, "net_derive_salting_principal: failed to obtain stanard DES salt\n");
1261                 return False;
1262         }
1263
1264         fstrcpy( salt, std_salt );
1265         SAFE_FREE( std_salt );
1266
1267         /* if it's a Windows functional domain, we have to look for the UPN */
1268
1269         if ( domain_func == DS_DOMAIN_FUNCTION_2000 ) {
1270                 char *upn = ads_get_upn(ads, ctx, machine_name);
1271                 if ( upn ) {
1272                         fstrcpy( salt, upn );
1273                 }
1274         }
1275
1276         return kerberos_secrets_store_des_salt( salt );
1277 }
1278
1279 /*******************************************************************
1280  Send a DNS update request
1281 *******************************************************************/
1282
1283 #if defined(WITH_DNS_UPDATES)
1284 #include "dns.h"
1285 DNS_ERROR DoDNSUpdate(char *pszServerName,
1286                       const char *pszDomainName, const char *pszHostName,
1287                       const struct sockaddr_storage *sslist,
1288                       size_t num_addrs );
1289
1290 static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
1291                                         const char *machine_name,
1292                                         const struct sockaddr_storage *addrs,
1293                                         int num_addrs)
1294 {
1295         struct dns_rr_ns *nameservers = NULL;
1296         int ns_count = 0;
1297         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
1298         DNS_ERROR dns_err;
1299         fstring dns_server;
1300         const char *dnsdomain = NULL;
1301         char *root_domain = NULL;
1302
1303         if ( (dnsdomain = strchr_m( machine_name, '.')) == NULL ) {
1304                 d_printf("No DNS domain configured for %s. "
1305                          "Unable to perform DNS Update.\n", machine_name);
1306                 status = NT_STATUS_INVALID_PARAMETER;
1307                 goto done;
1308         }
1309         dnsdomain++;
1310
1311         status = ads_dns_lookup_ns( ctx, dnsdomain, &nameservers, &ns_count );
1312         if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
1313                 /* Child domains often do not have NS records.  Look
1314                    for the NS record for the forest root domain
1315                    (rootDomainNamingContext in therootDSE) */
1316
1317                 const char *rootname_attrs[] =  { "rootDomainNamingContext", NULL };
1318                 LDAPMessage *msg = NULL;
1319                 char *root_dn;
1320                 ADS_STATUS ads_status;
1321
1322                 if ( !ads->ldap.ld ) {
1323                         ads_status = ads_connect( ads );
1324                         if ( !ADS_ERR_OK(ads_status) ) {
1325                                 DEBUG(0,("net_update_dns_internal: Failed to connect to our DC!\n"));
1326                                 goto done;
1327                         }
1328                 }
1329
1330                 ads_status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
1331                                        "(objectclass=*)", rootname_attrs, &msg);
1332                 if (!ADS_ERR_OK(ads_status)) {
1333                         goto done;
1334                 }
1335
1336                 root_dn = ads_pull_string(ads, ctx, msg,  "rootDomainNamingContext");
1337                 if ( !root_dn ) {
1338                         ads_msgfree( ads, msg );
1339                         goto done;
1340                 }
1341
1342                 root_domain = ads_build_domain( root_dn );
1343
1344                 /* cleanup */
1345                 ads_msgfree( ads, msg );
1346
1347                 /* try again for NS servers */
1348
1349                 status = ads_dns_lookup_ns( ctx, root_domain, &nameservers, &ns_count );
1350
1351                 if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
1352                         DEBUG(3,("net_ads_join: Failed to find name server for the %s "
1353                          "realm\n", ads->config.realm));
1354                         goto done;
1355                 }
1356
1357                 dnsdomain = root_domain;
1358
1359         }
1360
1361         /* Now perform the dns update - we'll try non-secure and if we fail,
1362            we'll follow it up with a secure update */
1363
1364         fstrcpy( dns_server, nameservers[0].hostname );
1365
1366         dns_err = DoDNSUpdate(dns_server, dnsdomain, machine_name, addrs, num_addrs);
1367         if (!ERR_DNS_IS_OK(dns_err)) {
1368                 status = NT_STATUS_UNSUCCESSFUL;
1369         }
1370
1371 done:
1372
1373         SAFE_FREE( root_domain );
1374
1375         return status;
1376 }
1377
1378 static NTSTATUS net_update_dns(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads)
1379 {
1380         int num_addrs;
1381         struct sockaddr_storage *iplist = NULL;
1382         fstring machine_name;
1383         NTSTATUS status;
1384
1385         name_to_fqdn( machine_name, global_myname() );
1386         strlower_m( machine_name );
1387
1388         /* Get our ip address (not the 127.0.0.x address but a real ip
1389          * address) */
1390
1391         num_addrs = get_my_ip_address( &iplist );
1392         if ( num_addrs <= 0 ) {
1393                 DEBUG(4,("net_update_dns: Failed to find my non-loopback IP "
1394                          "addresses!\n"));
1395                 return NT_STATUS_INVALID_PARAMETER;
1396         }
1397
1398         status = net_update_dns_internal(mem_ctx, ads, machine_name,
1399                                          iplist, num_addrs);
1400         SAFE_FREE( iplist );
1401         return status;
1402 }
1403 #endif
1404
1405
1406 /*******************************************************************
1407  ********************************************************************/
1408
1409 static int net_ads_join_usage(int argc, const char **argv)
1410 {
1411         d_printf("net ads join [options]\n");
1412         d_printf("Valid options:\n");
1413         d_printf("   createupn[=UPN]    Set the userPrincipalName attribute during the join.\n");
1414         d_printf("                      The deault UPN is in the form host/netbiosname@REALM.\n");
1415         d_printf("   createcomputer=OU  Precreate the computer account in a specific OU.\n");
1416         d_printf("                      The OU string read from top to bottom without RDNs and delimited by a '/'.\n");
1417         d_printf("                      E.g. \"createcomputer=Computers/Servers/Unix\"\n");
1418         d_printf("                      NB: A backslash '\\' is used as escape at multiple levels and may\n");
1419         d_printf("                          need to be doubled or even quadrupled.  It is not used as a separator.\n");
1420         d_printf("   osName=string      Set the operatingSystem attribute during the join.\n");
1421         d_printf("   osVer=string       Set the operatingSystemVersion attribute during the join.\n");
1422         d_printf("                      NB: osName and osVer must be specified together for either to take effect.\n");
1423         d_printf("                          Also, the operatingSystemService attribute is also set when along with\n");
1424         d_printf("                          the two other attributes.\n");
1425
1426         return -1;
1427 }
1428
1429 /*******************************************************************
1430  ********************************************************************/
1431
1432 int net_ads_join(int argc, const char **argv)
1433 {
1434         ADS_STRUCT *ads = NULL;
1435         ADS_STATUS status;
1436         NTSTATUS nt_status;
1437         const char *short_domain_name = NULL;
1438         char *tmp_password, *password;
1439         TALLOC_CTX *ctx = NULL;
1440         DOM_SID *domain_sid = NULL;
1441         bool createupn = False;
1442         const char *machineupn = NULL;
1443         const char *create_in_ou = NULL;
1444         int i;
1445         fstring dc_name;
1446         struct sockaddr_storage dcss;
1447         const char *os_name = NULL;
1448         const char *os_version = NULL;
1449
1450         nt_status = check_ads_config();
1451         if (!NT_STATUS_IS_OK(nt_status)) {
1452                 d_fprintf(stderr, "Invalid configuration.  Exiting....\n");
1453                 goto fail;
1454         }
1455
1456         /* find a DC to initialize the server affinity cache */
1457
1458         get_dc_name( lp_workgroup(), lp_realm(), dc_name, &dcss );
1459
1460         status = ads_startup(True, &ads);
1461         if (!ADS_ERR_OK(status)) {
1462                 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
1463                 nt_status = ads_ntstatus(status);
1464                 goto fail;
1465         }
1466
1467         if (strcmp(ads->config.realm, lp_realm()) != 0) {
1468                 d_fprintf(stderr, "realm of remote server (%s) and realm in %s "
1469                         "(%s) DO NOT match.  Aborting join\n",
1470                         ads->config.realm, get_dyn_CONFIGFILE(), lp_realm());
1471                 nt_status = NT_STATUS_INVALID_PARAMETER;
1472                 goto fail;
1473         }
1474
1475         if (!(ctx = talloc_init("net_ads_join"))) {
1476                 d_fprintf(stderr, "Could not initialise talloc context.\n");
1477                 nt_status = NT_STATUS_NO_MEMORY;
1478                 goto fail;
1479         }
1480
1481         /* process additional command line args */
1482
1483         for ( i=0; i<argc; i++ ) {
1484                 if ( !StrnCaseCmp(argv[i], "createupn", strlen("createupn")) ) {
1485                         createupn = True;
1486                         machineupn = get_string_param(argv[i]);
1487                 }
1488                 else if ( !StrnCaseCmp(argv[i], "createcomputer", strlen("createcomputer")) ) {
1489                         if ( (create_in_ou = get_string_param(argv[i])) == NULL ) {
1490                                 d_fprintf(stderr, "Please supply a valid OU path.\n");
1491                                 nt_status = NT_STATUS_INVALID_PARAMETER;
1492                                 goto fail;
1493                         }
1494                 }
1495                 else if ( !StrnCaseCmp(argv[i], "osName", strlen("osName")) ) {
1496                         if ( (os_name = get_string_param(argv[i])) == NULL ) {
1497                                 d_fprintf(stderr, "Please supply a operating system name.\n");
1498                                 nt_status = NT_STATUS_INVALID_PARAMETER;
1499                                 goto fail;
1500                         }
1501                 }
1502                 else if ( !StrnCaseCmp(argv[i], "osVer", strlen("osVer")) ) {
1503                         if ( (os_version = get_string_param(argv[i])) == NULL ) {
1504                                 d_fprintf(stderr, "Please supply a valid operating system version.\n");
1505                                 nt_status = NT_STATUS_INVALID_PARAMETER;
1506                                 goto fail;
1507                         }
1508                 }
1509                 else {
1510                         d_fprintf(stderr, "Bad option: %s\n", argv[i]);
1511                         nt_status = NT_STATUS_INVALID_PARAMETER;
1512                         goto fail;
1513                 }
1514         }
1515
1516         /* If we were given an OU, try to create the machine in
1517            the OU account first and then do the normal RPC join */
1518
1519         if  ( create_in_ou ) {
1520                 status = net_precreate_machine_acct( ads, create_in_ou );
1521                 if ( !ADS_ERR_OK(status) ) {
1522                         d_fprintf( stderr, "Failed to pre-create the machine object "
1523                                 "in OU %s.\n", create_in_ou);
1524                         DEBUG(1, ("error calling net_precreate_machine_acct: %s\n", 
1525                                   ads_errstr(status)));
1526                         nt_status = ads_ntstatus(status);
1527                         goto fail;
1528                 }
1529         }
1530
1531         /* Do the domain join here */
1532
1533         tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
1534         password = talloc_strdup(ctx, tmp_password);
1535
1536         nt_status = net_join_domain(ctx, ads->config.ldap_server_name,
1537                                     &ads->ldap.ss, &short_domain_name, &domain_sid, password);
1538         if ( !NT_STATUS_IS_OK(nt_status) ) {
1539                 DEBUG(1, ("call of net_join_domain failed: %s\n",
1540                           get_friendly_nt_error_msg(nt_status)));
1541                 goto fail;
1542         }
1543
1544         /* Check the short name of the domain */
1545
1546         if ( !strequal(lp_workgroup(), short_domain_name) ) {
1547                 d_printf("The workgroup in %s does not match the short\n", get_dyn_CONFIGFILE());
1548                 d_printf("domain name obtained from the server.\n");
1549                 d_printf("Using the name [%s] from the server.\n", short_domain_name);
1550                 d_printf("You should set \"workgroup = %s\" in %s.\n",
1551                          short_domain_name, get_dyn_CONFIGFILE());
1552         }
1553
1554         d_printf("Using short domain name -- %s\n", short_domain_name);
1555
1556         /*  HACK ALERT!  Store the sid and password under both the lp_workgroup() 
1557             value from smb.conf and the string returned from the server.  The former is
1558             neede to bootstrap winbindd's first connection to the DC to get the real 
1559             short domain name   --jerry */
1560
1561         if ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
1562                 || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
1563         {
1564                 /* issue an internal error here for now.
1565                  * everything else would mean changing tdb routines. */
1566                 nt_status = NT_STATUS_INTERNAL_ERROR;
1567                 goto fail;
1568         }
1569
1570         /* Verify that everything is ok */
1571
1572         nt_status = net_rpc_join_ok(short_domain_name,
1573                                     ads->config.ldap_server_name, &ads->ldap.ss);
1574         if (!NT_STATUS_IS_OK(nt_status)) {
1575                 d_fprintf(stderr,
1576                           "Failed to verify membership in domain: %s!\n",
1577                           nt_errstr(nt_status));
1578                 goto fail;
1579         }
1580
1581         /* create the dNSHostName & servicePrincipalName values */
1582
1583         status = net_set_machine_spn( ctx, ads );
1584         if ( !ADS_ERR_OK(status) )  {
1585
1586                 d_fprintf(stderr, "Failed to set servicePrincipalNames. Please ensure that\n");
1587                 d_fprintf(stderr, "the DNS domain of this server matches the AD domain,\n");
1588                 d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
1589
1590                 /* Disable the machine account in AD.  Better to fail than to leave 
1591                    a confused admin.  */
1592
1593                 if ( net_ads_leave( 0, NULL ) != 0 ) {
1594                         d_fprintf( stderr, "Failed to disable machine account in AD.  Please do so manually.\n");
1595                 }
1596
1597                 /* clear out the machine password */
1598
1599                 netdom_store_machine_account( lp_workgroup(), domain_sid, "" );
1600                 netdom_store_machine_account( short_domain_name, domain_sid, "" );
1601
1602                 nt_status = ads_ntstatus(status);
1603                 goto fail;
1604         }
1605
1606         if ( !net_derive_salting_principal( ctx, ads ) ) {
1607                 DEBUG(1,("Failed to determine salting principal\n"));
1608                 goto fail;
1609         }
1610
1611         if ( createupn ) {
1612                 char *upn;
1613
1614                 /* default to using the short UPN name */
1615                 if (!machineupn ) {
1616                         upn = talloc_asprintf(ctx,
1617                                         "host/%s@%s", global_myname(),
1618                                         ads->config.realm );
1619                         if (!upn) {
1620                                 nt_status = NT_STATUS_NO_MEMORY;
1621                                 goto fail;
1622                         }
1623                         machineupn = upn;
1624                 }
1625
1626                 status = net_set_machine_upn( ctx, ads, machineupn );
1627                 if ( !ADS_ERR_OK(status) )  {
1628                         d_fprintf(stderr, "Failed to set userPrincipalName.  Are you a Domain Admin?\n");
1629                 }
1630         }
1631
1632         /* Try to set the operatingSystem attributes if asked */
1633
1634         if ( os_name && os_version ) {
1635                 status = net_set_os_attributes( ctx, ads, os_name, os_version );
1636                 if ( !ADS_ERR_OK(status) )  {
1637                         d_fprintf(stderr, "Failed to set operatingSystem attributes.  "
1638                                   "Are you a Domain Admin?\n");
1639                 }
1640         }
1641
1642         /* Now build the keytab, using the same ADS connection */
1643
1644         if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
1645                 DEBUG(1,("Error creating host keytab!\n"));
1646         }
1647
1648 #if defined(WITH_DNS_UPDATES)
1649         /* We enter this block with user creds */
1650         ads_kdestroy( NULL );
1651         ads_destroy(&ads);
1652         ads = NULL;
1653
1654         if ( (ads = ads_init( lp_realm(), NULL, NULL )) != NULL ) {
1655                 /* kinit with the machine password */
1656
1657                 use_in_memory_ccache();
1658                 asprintf( &ads->auth.user_name, "%s$", global_myname() );
1659                 ads->auth.password = secrets_fetch_machine_password(
1660                         lp_workgroup(), NULL, NULL );
1661                 ads->auth.realm = SMB_STRDUP( lp_realm() );
1662                 ads_kinit_password( ads );
1663         }
1664
1665         if ( !ads || !NT_STATUS_IS_OK(net_update_dns( ctx, ads )) ) {
1666                 d_fprintf( stderr, "DNS update failed!\n" );
1667         }
1668
1669         /* exit from this block using machine creds */
1670 #endif
1671
1672         d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->server.realm);
1673
1674         TALLOC_FREE( ctx );
1675         ads_destroy(&ads);
1676
1677         return 0;
1678
1679 fail:
1680         /* issue an overall failure message at the end. */
1681         d_printf("Failed to join domain: %s\n", get_friendly_nt_error_msg(nt_status));
1682
1683         TALLOC_FREE( ctx );
1684         ads_destroy(&ads);
1685
1686         return -1;
1687
1688 }
1689
1690 /*******************************************************************
1691  ********************************************************************/
1692
1693 static int net_ads_dns_usage(int argc, const char **argv)
1694 {
1695 #if defined(WITH_DNS_UPDATES)
1696         d_printf("net ads dns <command>\n");
1697         d_printf("Valid commands:\n");
1698         d_printf("   register         Issue a dynamic DNS update request for our hostname\n");
1699
1700         return 0;
1701 #else
1702         d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1703         return -1;
1704 #endif
1705 }
1706
1707 /*******************************************************************
1708  ********************************************************************/
1709
1710 static int net_ads_dns_register(int argc, const char **argv)
1711 {
1712 #if defined(WITH_DNS_UPDATES)
1713         ADS_STRUCT *ads;
1714         ADS_STATUS status;
1715         TALLOC_CTX *ctx;
1716
1717 #ifdef DEVELOPER
1718         talloc_enable_leak_report();
1719 #endif
1720
1721         if (argc > 0) {
1722                 d_fprintf(stderr, "net ads dns register\n");
1723                 return -1;
1724         }
1725
1726         if (!(ctx = talloc_init("net_ads_dns"))) {
1727                 d_fprintf(stderr, "Could not initialise talloc context\n");
1728                 return -1;
1729         }
1730
1731         status = ads_startup(True, &ads);
1732         if ( !ADS_ERR_OK(status) ) {
1733                 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
1734                 TALLOC_FREE(ctx);
1735                 return -1;
1736         }
1737
1738         if ( !NT_STATUS_IS_OK(net_update_dns(ctx, ads)) ) {
1739                 d_fprintf( stderr, "DNS update failed!\n" );
1740                 ads_destroy( &ads );
1741                 TALLOC_FREE( ctx );
1742                 return -1;
1743         }
1744
1745         d_fprintf( stderr, "Successfully registered hostname with DNS\n" );
1746
1747         ads_destroy(&ads);
1748         TALLOC_FREE( ctx );
1749
1750         return 0;
1751 #else
1752         d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1753         return -1;
1754 #endif
1755 }
1756
1757 #if defined(WITH_DNS_UPDATES)
1758 DNS_ERROR do_gethostbyname(const char *server, const char *host);
1759 #endif
1760
1761 static int net_ads_dns_gethostbyname(int argc, const char **argv)
1762 {
1763 #if defined(WITH_DNS_UPDATES)
1764         DNS_ERROR err;
1765
1766 #ifdef DEVELOPER
1767         talloc_enable_leak_report();
1768 #endif
1769
1770         if (argc != 2) {
1771                 d_fprintf(stderr, "net ads dns gethostbyname <server> "
1772                           "<name>\n");
1773                 return -1;
1774         }
1775
1776         err = do_gethostbyname(argv[0], argv[1]);
1777
1778         d_printf("do_gethostbyname returned %d\n", ERROR_DNS_V(err));
1779 #endif
1780         return 0;
1781 }
1782
1783 static int net_ads_dns(int argc, const char *argv[])
1784 {
1785         struct functable func[] = {
1786                 {"REGISTER", net_ads_dns_register},
1787                 {"GETHOSTBYNAME", net_ads_dns_gethostbyname},
1788                 {NULL, NULL}
1789         };
1790
1791         return net_run_function(argc, argv, func, net_ads_dns_usage);
1792 }
1793
1794 /*******************************************************************
1795  ********************************************************************/
1796
1797 int net_ads_printer_usage(int argc, const char **argv)
1798 {
1799         d_printf(
1800 "\nnet ads printer search <printer>"
1801 "\n\tsearch for a printer in the directory\n"
1802 "\nnet ads printer info <printer> <server>"
1803 "\n\tlookup info in directory for printer on server"
1804 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
1805 "\nnet ads printer publish <printername>"
1806 "\n\tpublish printer in directory"
1807 "\n\t(note: printer name is required)\n"
1808 "\nnet ads printer remove <printername>"
1809 "\n\tremove printer from directory"
1810 "\n\t(note: printer name is required)\n");
1811         return -1;
1812 }
1813
1814 /*******************************************************************
1815  ********************************************************************/
1816
1817 static int net_ads_printer_search(int argc, const char **argv)
1818 {
1819         ADS_STRUCT *ads;
1820         ADS_STATUS rc;
1821         LDAPMessage *res = NULL;
1822
1823         if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1824                 return -1;
1825         }
1826
1827         rc = ads_find_printers(ads, &res);
1828
1829         if (!ADS_ERR_OK(rc)) {
1830                 d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
1831                 ads_msgfree(ads, res);
1832                 ads_destroy(&ads);
1833                 return -1;
1834         }
1835
1836         if (ads_count_replies(ads, res) == 0) {
1837                 d_fprintf(stderr, "No results found\n");
1838                 ads_msgfree(ads, res);
1839                 ads_destroy(&ads);
1840                 return -1;
1841         }
1842
1843         ads_dump(ads, res);
1844         ads_msgfree(ads, res);
1845         ads_destroy(&ads);
1846         return 0;
1847 }
1848
1849 static int net_ads_printer_info(int argc, const char **argv)
1850 {
1851         ADS_STRUCT *ads;
1852         ADS_STATUS rc;
1853         const char *servername, *printername;
1854         LDAPMessage *res = NULL;
1855
1856         if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1857                 return -1;
1858         }
1859
1860         if (argc > 0) {
1861                 printername = argv[0];
1862         } else {
1863                 printername = "*";
1864         }
1865
1866         if (argc > 1) {
1867                 servername =  argv[1];
1868         } else {
1869                 servername = global_myname();
1870         }
1871
1872         rc = ads_find_printer_on_server(ads, &res, printername, servername);
1873
1874         if (!ADS_ERR_OK(rc)) {
1875                 d_fprintf(stderr, "Server '%s' not found: %s\n",
1876                         servername, ads_errstr(rc));
1877                 ads_msgfree(ads, res);
1878                 ads_destroy(&ads);
1879                 return -1;
1880         }
1881
1882         if (ads_count_replies(ads, res) == 0) {
1883                 d_fprintf(stderr, "Printer '%s' not found\n", printername);
1884                 ads_msgfree(ads, res);
1885                 ads_destroy(&ads);
1886                 return -1;
1887         }
1888
1889         ads_dump(ads, res);
1890         ads_msgfree(ads, res);
1891         ads_destroy(&ads);
1892
1893         return 0;
1894 }
1895
1896 static int net_ads_printer_publish(int argc, const char **argv)
1897 {
1898         ADS_STRUCT *ads;
1899         ADS_STATUS rc;
1900         const char *servername, *printername;
1901         struct cli_state *cli;
1902         struct rpc_pipe_client *pipe_hnd;
1903         struct sockaddr_storage server_ss;
1904         NTSTATUS nt_status;
1905         TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
1906         ADS_MODLIST mods = ads_init_mods(mem_ctx);
1907         char *prt_dn, *srv_dn, **srv_cn;
1908         char *srv_cn_escaped = NULL, *printername_escaped = NULL;
1909         LDAPMessage *res = NULL;
1910
1911         if (!ADS_ERR_OK(ads_startup(True, &ads))) {
1912                 talloc_destroy(mem_ctx);
1913                 return -1;
1914         }
1915
1916         if (argc < 1) {
1917                 talloc_destroy(mem_ctx);
1918                 return net_ads_printer_usage(argc, argv);
1919         }
1920
1921         printername = argv[0];
1922
1923         if (argc == 2) {
1924                 servername = argv[1];
1925         } else {
1926                 servername = global_myname();
1927         }
1928
1929         /* Get printer data from SPOOLSS */
1930
1931         resolve_name(servername, &server_ss, 0x20);
1932
1933         nt_status = cli_full_connection(&cli, global_myname(), servername,
1934                                         &server_ss, 0,
1935                                         "IPC$", "IPC",
1936                                         opt_user_name, opt_workgroup,
1937                                         opt_password ? opt_password : "",
1938                                         CLI_FULL_CONNECTION_USE_KERBEROS,
1939                                         Undefined, NULL);
1940
1941         if (NT_STATUS_IS_ERR(nt_status)) {
1942                 d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
1943                          "for %s\n", servername, printername);
1944                 ads_destroy(&ads);
1945                 talloc_destroy(mem_ctx);
1946                 return -1;
1947         }
1948
1949         /* Publish on AD server */
1950
1951         ads_find_machine_acct(ads, &res, servername);
1952
1953         if (ads_count_replies(ads, res) == 0) {
1954                 d_fprintf(stderr, "Could not find machine account for server %s\n", 
1955                          servername);
1956                 ads_destroy(&ads);
1957                 talloc_destroy(mem_ctx);
1958                 return -1;
1959         }
1960
1961         srv_dn = ldap_get_dn((LDAP *)ads->ldap.ld, (LDAPMessage *)res);
1962         srv_cn = ldap_explode_dn(srv_dn, 1);
1963
1964         srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn[0]);
1965         printername_escaped = escape_rdn_val_string_alloc(printername);
1966         if (!srv_cn_escaped || !printername_escaped) {
1967                 SAFE_FREE(srv_cn_escaped);
1968                 SAFE_FREE(printername_escaped);
1969                 d_fprintf(stderr, "Internal error, out of memory!");
1970                 ads_destroy(&ads);
1971                 talloc_destroy(mem_ctx);
1972                 return -1;
1973         }
1974
1975         asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn_escaped, printername_escaped, srv_dn);
1976
1977         SAFE_FREE(srv_cn_escaped);
1978         SAFE_FREE(printername_escaped);
1979
1980         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
1981         if (!pipe_hnd) {
1982                 d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
1983                          servername);
1984                 SAFE_FREE(prt_dn);
1985                 ads_destroy(&ads);
1986                 talloc_destroy(mem_ctx);
1987                 return -1;
1988         }
1989
1990         if (!W_ERROR_IS_OK(get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
1991                                                               printername))) {
1992                 SAFE_FREE(prt_dn);
1993                 ads_destroy(&ads);
1994                 talloc_destroy(mem_ctx);
1995                 return -1;
1996         }
1997
1998         rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
1999         if (!ADS_ERR_OK(rc)) {
2000                 d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
2001                 SAFE_FREE(prt_dn);
2002                 ads_destroy(&ads);
2003                 talloc_destroy(mem_ctx);
2004                 return -1;
2005         }
2006
2007         d_printf("published printer\n");
2008         SAFE_FREE(prt_dn);
2009         ads_destroy(&ads);
2010         talloc_destroy(mem_ctx);
2011
2012         return 0;
2013 }
2014
2015 static int net_ads_printer_remove(int argc, const char **argv)
2016 {
2017         ADS_STRUCT *ads;
2018         ADS_STATUS rc;
2019         const char *servername;
2020         char *prt_dn;
2021         LDAPMessage *res = NULL;
2022
2023         if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2024                 return -1;
2025         }
2026
2027         if (argc < 1) {
2028                 return net_ads_printer_usage(argc, argv);
2029         }
2030
2031         if (argc > 1) {
2032                 servername = argv[1];
2033         } else {
2034                 servername = global_myname();
2035         }
2036
2037         rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
2038
2039         if (!ADS_ERR_OK(rc)) {
2040                 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
2041                 ads_msgfree(ads, res);
2042                 ads_destroy(&ads);
2043                 return -1;
2044         }
2045
2046         if (ads_count_replies(ads, res) == 0) {
2047                 d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
2048                 ads_msgfree(ads, res);
2049                 ads_destroy(&ads);
2050                 return -1;
2051         }
2052
2053         prt_dn = ads_get_dn(ads, res);
2054         ads_msgfree(ads, res);
2055         rc = ads_del_dn(ads, prt_dn);
2056         ads_memfree(ads, prt_dn);
2057
2058         if (!ADS_ERR_OK(rc)) {
2059                 d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
2060                 ads_destroy(&ads);
2061                 return -1;
2062         }
2063
2064         ads_destroy(&ads);
2065         return 0;
2066 }
2067
2068 static int net_ads_printer(int argc, const char **argv)
2069 {
2070         struct functable func[] = {
2071                 {"SEARCH", net_ads_printer_search},
2072                 {"INFO", net_ads_printer_info},
2073                 {"PUBLISH", net_ads_printer_publish},
2074                 {"REMOVE", net_ads_printer_remove},
2075                 {NULL, NULL}
2076         };
2077
2078         return net_run_function(argc, argv, func, net_ads_printer_usage);
2079 }
2080
2081
2082 static int net_ads_password(int argc, const char **argv)
2083 {
2084         ADS_STRUCT *ads;
2085         const char *auth_principal = opt_user_name;
2086         const char *auth_password = opt_password;
2087         char *realm = NULL;
2088         char *new_password = NULL;
2089         char *c, *prompt;
2090         const char *user;
2091         ADS_STATUS ret;
2092
2093         if (opt_user_name == NULL || opt_password == NULL) {
2094                 d_fprintf(stderr, "You must supply an administrator username/password\n");
2095                 return -1;
2096         }
2097
2098         if (argc < 1) {
2099                 d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
2100                 return -1;
2101         }
2102
2103         user = argv[0];
2104         if (!strchr_m(user, '@')) {
2105                 asprintf(&c, "%s@%s", argv[0], lp_realm());
2106                 user = c;
2107         }
2108
2109         use_in_memory_ccache();
2110         c = strchr_m(auth_principal, '@');
2111         if (c) {
2112                 realm = ++c;
2113         } else {
2114                 realm = lp_realm();
2115         }
2116
2117         /* use the realm so we can eventually change passwords for users
2118         in realms other than default */
2119         if (!(ads = ads_init(realm, opt_workgroup, opt_host))) {
2120                 return -1;
2121         }
2122
2123         /* we don't actually need a full connect, but it's the easy way to
2124                 fill in the KDC's addresss */
2125         ads_connect(ads);
2126
2127         if (!ads || !ads->config.realm) {
2128                 d_fprintf(stderr, "Didn't find the kerberos server!\n");
2129                 return -1;
2130         }
2131
2132         if (argv[1]) {
2133                 new_password = (char *)argv[1];
2134         } else {
2135                 asprintf(&prompt, "Enter new password for %s:", user);
2136                 new_password = getpass(prompt);
2137                 free(prompt);
2138         }
2139
2140         ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
2141                                 auth_password, user, new_password, ads->auth.time_offset);
2142         if (!ADS_ERR_OK(ret)) {
2143                 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
2144                 ads_destroy(&ads);
2145                 return -1;
2146         }
2147
2148         d_printf("Password change for %s completed.\n", user);
2149         ads_destroy(&ads);
2150
2151         return 0;
2152 }
2153
2154 int net_ads_changetrustpw(int argc, const char **argv)
2155 {
2156         ADS_STRUCT *ads;
2157         char *host_principal;
2158         fstring my_name;
2159         ADS_STATUS ret;
2160
2161         if (!secrets_init()) {
2162                 DEBUG(1,("Failed to initialise secrets database\n"));
2163                 return -1;
2164         }
2165
2166         net_use_krb_machine_account();
2167
2168         use_in_memory_ccache();
2169
2170         if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2171                 return -1;
2172         }
2173
2174         fstrcpy(my_name, global_myname());
2175         strlower_m(my_name);
2176         asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
2177         d_printf("Changing password for principal: %s\n", host_principal);
2178
2179         ret = ads_change_trust_account_password(ads, host_principal);
2180
2181         if (!ADS_ERR_OK(ret)) {
2182                 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
2183                 ads_destroy(&ads);
2184                 SAFE_FREE(host_principal);
2185                 return -1;
2186         }
2187
2188         d_printf("Password change for principal %s succeeded.\n", host_principal);
2189
2190         if (lp_use_kerberos_keytab()) {
2191                 d_printf("Attempting to update system keytab with new password.\n");
2192                 if (ads_keytab_create_default(ads)) {
2193                         d_printf("Failed to update system keytab.\n");
2194                 }
2195         }
2196
2197         ads_destroy(&ads);
2198         SAFE_FREE(host_principal);
2199
2200         return 0;
2201 }
2202
2203 /*
2204   help for net ads search
2205 */
2206 static int net_ads_search_usage(int argc, const char **argv)
2207 {
2208         d_printf(
2209                 "\nnet ads search <expression> <attributes...>\n"\
2210                 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2211                 "The expression is a standard LDAP search expression, and the\n"\
2212                 "attributes are a list of LDAP fields to show in the results\n\n"\
2213                 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
2214                 );
2215         net_common_flags_usage(argc, argv);
2216         return -1;
2217 }
2218
2219
2220 /*
2221   general ADS search function. Useful in diagnosing problems in ADS
2222 */
2223 static int net_ads_search(int argc, const char **argv)
2224 {
2225         ADS_STRUCT *ads;
2226         ADS_STATUS rc;
2227         const char *ldap_exp;
2228         const char **attrs;
2229         LDAPMessage *res = NULL;
2230
2231         if (argc < 1) {
2232                 return net_ads_search_usage(argc, argv);
2233         }
2234
2235         if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2236                 return -1;
2237         }
2238
2239         ldap_exp = argv[0];
2240         attrs = (argv + 1);
2241
2242         rc = ads_do_search_all(ads, ads->config.bind_path,
2243                                LDAP_SCOPE_SUBTREE,
2244                                ldap_exp, attrs, &res);
2245         if (!ADS_ERR_OK(rc)) {
2246                 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2247                 ads_destroy(&ads);
2248                 return -1;
2249         }
2250
2251         d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2252
2253         /* dump the results */
2254         ads_dump(ads, res);
2255
2256         ads_msgfree(ads, res);
2257         ads_destroy(&ads);
2258
2259         return 0;
2260 }
2261
2262
2263 /*
2264   help for net ads search
2265 */
2266 static int net_ads_dn_usage(int argc, const char **argv)
2267 {
2268         d_printf(
2269                 "\nnet ads dn <dn> <attributes...>\n"\
2270                 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2271                 "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
2272                 "to show in the results\n\n"\
2273                 "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
2274                 "Note: the DN must be provided properly escaped. See RFC 4514 for details\n\n"
2275                 );
2276         net_common_flags_usage(argc, argv);
2277         return -1;
2278 }
2279
2280
2281 /*
2282   general ADS search function. Useful in diagnosing problems in ADS
2283 */
2284 static int net_ads_dn(int argc, const char **argv)
2285 {
2286         ADS_STRUCT *ads;
2287         ADS_STATUS rc;
2288         const char *dn;
2289         const char **attrs;
2290         LDAPMessage *res = NULL;
2291
2292         if (argc < 1) {
2293                 return net_ads_dn_usage(argc, argv);
2294         }
2295
2296         if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2297                 return -1;
2298         }
2299
2300         dn = argv[0];
2301         attrs = (argv + 1);
2302
2303         rc = ads_do_search_all(ads, dn,
2304                                LDAP_SCOPE_BASE,
2305                                "(objectclass=*)", attrs, &res);
2306         if (!ADS_ERR_OK(rc)) {
2307                 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2308                 ads_destroy(&ads);
2309                 return -1;
2310         }
2311
2312         d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2313
2314         /* dump the results */
2315         ads_dump(ads, res);
2316
2317         ads_msgfree(ads, res);
2318         ads_destroy(&ads);
2319
2320         return 0;
2321 }
2322
2323 /*
2324   help for net ads sid search
2325 */
2326 static int net_ads_sid_usage(int argc, const char **argv)
2327 {
2328         d_printf(
2329                 "\nnet ads sid <sid> <attributes...>\n"\
2330                 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2331                 "The SID is in string format, and the attributes are a list of LDAP fields \n"\
2332                 "to show in the results\n\n"\
2333                 "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
2334                 );
2335         net_common_flags_usage(argc, argv);
2336         return -1;
2337 }
2338
2339
2340 /*
2341   general ADS search function. Useful in diagnosing problems in ADS
2342 */
2343 static int net_ads_sid(int argc, const char **argv)
2344 {
2345         ADS_STRUCT *ads;
2346         ADS_STATUS rc;
2347         const char *sid_string;
2348         const char **attrs;
2349         LDAPMessage *res = NULL;
2350         DOM_SID sid;
2351
2352         if (argc < 1) {
2353                 return net_ads_sid_usage(argc, argv);
2354         }
2355
2356         if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2357                 return -1;
2358         }
2359
2360         sid_string = argv[0];
2361         attrs = (argv + 1);
2362
2363         if (!string_to_sid(&sid, sid_string)) {
2364                 d_fprintf(stderr, "could not convert sid\n");
2365                 ads_destroy(&ads);
2366                 return -1;
2367         }
2368
2369         rc = ads_search_retry_sid(ads, &res, &sid, attrs);
2370         if (!ADS_ERR_OK(rc)) {
2371                 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2372                 ads_destroy(&ads);
2373                 return -1;
2374         }
2375
2376         d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2377
2378         /* dump the results */
2379         ads_dump(ads, res);
2380
2381         ads_msgfree(ads, res);
2382         ads_destroy(&ads);
2383
2384         return 0;
2385 }
2386
2387
2388 static int net_ads_keytab_usage(int argc, const char **argv)
2389 {
2390         d_printf(
2391                 "net ads keytab <COMMAND>\n"\
2392 "<COMMAND> can be either:\n"\
2393 "  ADD       Adds new service principal\n"\
2394 "  CREATE    Creates a fresh keytab\n"\
2395 "  FLUSH     Flushes out all keytab entries\n"\
2396 "  HELP      Prints this help message\n"\
2397 "  LIST      List the keytab\n"\
2398 "The ADD and LIST command will take arguments, the other commands\n"\
2399 "will not take any arguments.   The arguments given to ADD\n"\
2400 "should be a list of principals to add.  For example, \n"\
2401 "   net ads keytab add srv1 srv2\n"\
2402 "will add principals for the services srv1 and srv2 to the\n"\
2403 "system's keytab.\n"\
2404 "The LIST command takes a keytabname.\n"\
2405 "\n"
2406                 );
2407         return -1;
2408 }
2409
2410 static int net_ads_keytab_flush(int argc, const char **argv)
2411 {
2412         int ret;
2413         ADS_STRUCT *ads;
2414
2415         if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2416                 return -1;
2417         }
2418         ret = ads_keytab_flush(ads);
2419         ads_destroy(&ads);
2420         return ret;
2421 }
2422
2423 static int net_ads_keytab_add(int argc, const char **argv)
2424 {
2425         int i;
2426         int ret = 0;
2427         ADS_STRUCT *ads;
2428
2429         d_printf("Processing principals to add...\n");
2430         if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2431                 return -1;
2432         }
2433         for (i = 0; i < argc; i++) {
2434                 ret |= ads_keytab_add_entry(ads, argv[i]);
2435         }
2436         ads_destroy(&ads);
2437         return ret;
2438 }
2439
2440 static int net_ads_keytab_create(int argc, const char **argv)
2441 {
2442         ADS_STRUCT *ads;
2443         int ret;
2444
2445         if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2446                 return -1;
2447         }
2448         ret = ads_keytab_create_default(ads);
2449         ads_destroy(&ads);
2450         return ret;
2451 }
2452
2453 static int net_ads_keytab_list(int argc, const char **argv)
2454 {
2455         const char *keytab = NULL;
2456
2457         if (argc >= 1) {
2458                 keytab = argv[0];
2459         }
2460
2461         return ads_keytab_list(keytab);
2462 }
2463
2464
2465 int net_ads_keytab(int argc, const char **argv)
2466 {
2467         struct functable func[] = {
2468                 {"ADD", net_ads_keytab_add},
2469                 {"CREATE", net_ads_keytab_create},
2470                 {"FLUSH", net_ads_keytab_flush},
2471                 {"HELP", net_ads_keytab_usage},
2472                 {"LIST", net_ads_keytab_list},
2473                 {NULL, NULL}
2474         };
2475
2476         if (!lp_use_kerberos_keytab()) {
2477                 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
2478 use keytab functions.\n");
2479         }
2480
2481         return net_run_function(argc, argv, func, net_ads_keytab_usage);
2482 }
2483
2484 static int net_ads_kerberos_usage(int argc, const char **argv)
2485 {
2486         d_printf(
2487                 "net ads kerberos <COMMAND>\n"\
2488                 "<COMMAND> can be either:\n"\
2489                 "  RENEW     Renew TGT from existing credential cache\n"\
2490                 "  PAC       Dumps the Kerberos PAC\n"\
2491                 "  KINIT     Retrieve Ticket Granting Ticket (TGT)\n"\
2492                 "\n"
2493         );
2494
2495         return -1;
2496 }
2497
2498 static int net_ads_kerberos_renew(int argc, const char **argv)
2499 {
2500         int ret = smb_krb5_renew_ticket(NULL, NULL, NULL, NULL);
2501         if (ret) {
2502                 d_printf("failed to renew kerberos ticket: %s\n",
2503                         error_message(ret));
2504         }
2505         return ret;
2506 }
2507
2508 static int net_ads_kerberos_pac(int argc, const char **argv)
2509 {
2510         struct PAC_DATA *pac = NULL;
2511         struct PAC_LOGON_INFO *info = NULL;
2512         TALLOC_CTX *mem_ctx = NULL;
2513         NTSTATUS status;
2514         int ret = -1;
2515
2516         mem_ctx = talloc_init("net_ads_kerberos_pac");
2517         if (!mem_ctx) {
2518                 goto out;
2519         }
2520
2521         opt_password = net_prompt_pass(opt_user_name);
2522
2523         status = kerberos_return_pac(mem_ctx,
2524                                      opt_user_name,
2525                                      opt_password,
2526                                      0,
2527                                      NULL,
2528                                      NULL,
2529                                      NULL,
2530                                      True,
2531                                      True,
2532                                      2592000, /* one month */
2533                                      &pac);
2534         if (!NT_STATUS_IS_OK(status)) {
2535                 d_printf("failed to query kerberos PAC: %s\n",
2536                         nt_errstr(status));
2537                 goto out;
2538         }
2539
2540         info = get_logon_info_from_pac(pac);
2541         if (info) {
2542                 const char *s;
2543                 s = NDR_PRINT_STRUCT_STRING(mem_ctx, PAC_LOGON_INFO, info);
2544                 d_printf("The Pac: %s\n", s);
2545         }
2546
2547         ret = 0;
2548  out:
2549         TALLOC_FREE(mem_ctx);
2550         return ret;
2551 }
2552
2553 static int net_ads_kerberos_kinit(int argc, const char **argv)
2554 {
2555         TALLOC_CTX *mem_ctx = NULL;
2556         int ret = -1;
2557         NTSTATUS status;
2558
2559         mem_ctx = talloc_init("net_ads_kerberos_kinit");
2560         if (!mem_ctx) {
2561                 goto out;
2562         }
2563
2564         opt_password = net_prompt_pass(opt_user_name);
2565
2566         ret = kerberos_kinit_password_ext(opt_user_name,
2567                                           opt_password,
2568                                           0,
2569                                           NULL,
2570                                           NULL,
2571                                           NULL,
2572                                           True,
2573                                           True,
2574                                           2592000, /* one month */
2575                                           &status);
2576         if (ret) {
2577                 d_printf("failed to kinit password: %s\n",
2578                         nt_errstr(status));
2579         }
2580  out:
2581         return ret;
2582 }
2583
2584 int net_ads_kerberos(int argc, const char **argv)
2585 {
2586         struct functable func[] = {
2587                 {"KINIT", net_ads_kerberos_kinit},
2588                 {"RENEW", net_ads_kerberos_renew},
2589                 {"PAC", net_ads_kerberos_pac},
2590                 {"HELP", net_ads_kerberos_usage},
2591                 {NULL, NULL}
2592         };
2593
2594         return net_run_function(argc, argv, func, net_ads_kerberos_usage);
2595 }
2596
2597
2598 int net_ads_help(int argc, const char **argv)
2599 {
2600         struct functable func[] = {
2601                 {"USER", net_ads_user_usage},
2602                 {"GROUP", net_ads_group_usage},
2603                 {"PRINTER", net_ads_printer_usage},
2604                 {"SEARCH", net_ads_search_usage},
2605                 {"INFO", net_ads_info},
2606                 {"JOIN", net_ads_join_usage},
2607                 {"DNS", net_ads_dns_usage},
2608                 {"LEAVE", net_ads_leave},
2609                 {"STATUS", net_ads_status},
2610                 {"PASSWORD", net_ads_password},
2611                 {"CHANGETRUSTPW", net_ads_changetrustpw},
2612                 {NULL, NULL}
2613         };
2614
2615         return net_run_function(argc, argv, func, net_ads_usage);
2616 }
2617
2618 int net_ads(int argc, const char **argv)
2619 {
2620         struct functable func[] = {
2621                 {"INFO", net_ads_info},
2622                 {"JOIN", net_ads_join},
2623                 {"TESTJOIN", net_ads_testjoin},
2624                 {"LEAVE", net_ads_leave},
2625                 {"STATUS", net_ads_status},
2626                 {"USER", net_ads_user},
2627                 {"GROUP", net_ads_group},
2628                 {"DNS", net_ads_dns},
2629                 {"PASSWORD", net_ads_password},
2630                 {"CHANGETRUSTPW", net_ads_changetrustpw},
2631                 {"PRINTER", net_ads_printer},
2632                 {"SEARCH", net_ads_search},
2633                 {"DN", net_ads_dn},
2634                 {"SID", net_ads_sid},
2635                 {"WORKGROUP", net_ads_workgroup},
2636                 {"LOOKUP", net_ads_lookup},
2637                 {"KEYTAB", net_ads_keytab},
2638                 {"GPO", net_ads_gpo},
2639                 {"KERBEROS", net_ads_kerberos},
2640                 {"HELP", net_ads_help},
2641                 {NULL, NULL}
2642         };
2643
2644         return net_run_function(argc, argv, func, net_ads_usage);
2645 }
2646
2647 #else
2648
2649 static int net_ads_noads(void)
2650 {
2651         d_fprintf(stderr, "ADS support not compiled in\n");
2652         return -1;
2653 }
2654
2655 int net_ads_keytab(int argc, const char **argv)
2656 {
2657         return net_ads_noads();
2658 }
2659
2660 int net_ads_kerberos(int argc, const char **argv)
2661 {
2662         return net_ads_noads();
2663 }
2664
2665 int net_ads_usage(int argc, const char **argv)
2666 {
2667         return net_ads_noads();
2668 }
2669
2670 int net_ads_help(int argc, const char **argv)
2671 {
2672         return net_ads_noads();
2673 }
2674
2675 int net_ads_changetrustpw(int argc, const char **argv)
2676 {
2677         return net_ads_noads();
2678 }
2679
2680 int net_ads_join(int argc, const char **argv)
2681 {
2682         return net_ads_noads();
2683 }
2684
2685 int net_ads_user(int argc, const char **argv)
2686 {
2687         return net_ads_noads();
2688 }
2689
2690 int net_ads_group(int argc, const char **argv)
2691 {
2692         return net_ads_noads();
2693 }
2694
2695 /* this one shouldn't display a message */
2696 int net_ads_check(void)
2697 {
2698         return -1;
2699 }
2700
2701 int net_ads_check_our_domain(void)
2702 {
2703         return -1;
2704 }
2705
2706 int net_ads(int argc, const char **argv)
2707 {
2708         return net_ads_usage(argc, argv);
2709 }
2710
2711 #endif  /* WITH_ADS */