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