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