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