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