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