r9794: r11627@blu: tridge | 2005-08-30 22:55:27 +1000
[jra/samba/.git] / source4 / lib / credentials.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Copyright (C) Jelmer Vernooij 2005
5    Copyright (C) Tim Potter 2001
6    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "system/filesys.h"
25 #include "include/secrets.h"
26 #include "lib/ldb/include/ldb.h"
27 #include "librpc/gen_ndr/ndr_samr.h" /* for struct samrPassword */
28 #include "system/kerberos.h"
29 #include "auth/kerberos/kerberos.h"
30
31
32 /**
33  * Create a new credentials structure
34  * @param mem_ctx TALLOC_CTX parent for credentials structure 
35  */
36 struct cli_credentials *cli_credentials_init(TALLOC_CTX *mem_ctx) 
37 {
38         struct cli_credentials *cred = talloc(mem_ctx, struct cli_credentials);
39         if (!cred) {
40                 return cred;
41         }
42
43         cred->netlogon_creds = NULL;
44         cred->machine_account_pending = False;
45         cred->workstation_obtained = CRED_UNINITIALISED;
46         cred->username_obtained = CRED_UNINITIALISED;
47         cred->password_obtained = CRED_UNINITIALISED;
48         cred->domain_obtained = CRED_UNINITIALISED;
49         cred->realm_obtained = CRED_UNINITIALISED;
50         cred->ccache_obtained = CRED_UNINITIALISED;
51         cred->principal_obtained = CRED_UNINITIALISED;
52         return cred;
53 }
54
55 /**
56  * Obtain the username for this credentials context.
57  * @param cred credentials context
58  * @retval The username set on this context.
59  * @note Return value will never be NULL except by programmer error.
60  */
61 const char *cli_credentials_get_username(struct cli_credentials *cred, TALLOC_CTX *mem_ctx)
62 {
63         if (cred->machine_account_pending) {
64                 cli_credentials_set_machine_account(cred);
65         }
66
67         /* If we have a principal set on this, we want to login with "" domain and user@realm */
68         if (cred->username_obtained < cred->principal_obtained) {
69                 return cli_credentials_get_principal(cred, mem_ctx);
70         }
71
72         if (cred->username_obtained == CRED_CALLBACK) {
73                 cred->username = cred->username_cb(cred);
74                 cred->username_obtained = CRED_SPECIFIED;
75         }
76
77         return talloc_reference(mem_ctx, cred->username);
78 }
79
80 BOOL cli_credentials_set_username(struct cli_credentials *cred, 
81                                   const char *val, enum credentials_obtained obtained)
82 {
83         if (obtained >= cred->username_obtained) {
84                 cred->username = talloc_strdup(cred, val);
85                 cred->username_obtained = obtained;
86                 return True;
87         }
88
89         return False;
90 }
91
92 BOOL cli_credentials_set_username_callback(struct cli_credentials *cred,
93                                   const char *(*username_cb) (struct cli_credentials *))
94 {
95         if (cred->username_obtained < CRED_CALLBACK) {
96                 cred->username_cb = username_cb;
97                 cred->username_obtained = CRED_CALLBACK;
98                 return True;
99         }
100
101         return False;
102 }
103
104
105
106 /**
107  * Obtain the client principal for this credentials context.
108  * @param cred credentials context
109  * @retval The username set on this context.
110  * @note Return value will never be NULL except by programmer error.
111  */
112 const char *cli_credentials_get_principal(struct cli_credentials *cred, TALLOC_CTX *mem_ctx)
113 {
114         if (cred->machine_account_pending) {
115                 cli_credentials_set_machine_account(cred);
116         }
117
118         if (cred->principal_obtained == CRED_CALLBACK) {
119                 cred->principal = cred->principal_cb(cred);
120                 cred->principal_obtained = CRED_SPECIFIED;
121         }
122
123         if (cred->principal_obtained < cred->username_obtained) {
124                 return talloc_asprintf(mem_ctx, "%s@%s", 
125                                        cli_credentials_get_username(cred, mem_ctx),
126                                        cli_credentials_get_realm(cred));
127         }
128         return talloc_reference(mem_ctx, cred->principal);
129 }
130
131 BOOL cli_credentials_set_principal(struct cli_credentials *cred, const char *val, enum credentials_obtained obtained)
132 {
133         if (obtained >= cred->principal_obtained) {
134                 cred->principal = talloc_strdup(cred, val);
135                 cred->principal_obtained = obtained;
136                 return True;
137         }
138
139         return False;
140 }
141
142 BOOL cli_credentials_set_principal_callback(struct cli_credentials *cred,
143                                   const char *(*principal_cb) (struct cli_credentials *))
144 {
145         if (cred->principal_obtained < CRED_CALLBACK) {
146                 cred->principal_cb = principal_cb;
147                 cred->principal_obtained = CRED_CALLBACK;
148                 return True;
149         }
150
151         return False;
152 }
153
154 BOOL cli_credentials_authentication_requested(struct cli_credentials *cred) 
155 {
156         if (cred->principal_obtained >= CRED_SPECIFIED) {
157                 return True;
158         }
159         if (cred->username_obtained >= CRED_SPECIFIED) {
160                 return True;
161         }
162         return False;
163 }
164
165 /**
166  * Obtain the password for this credentials context.
167  * @param cred credentials context
168  * @retval If set, the cleartext password, otherwise NULL
169  */
170 const char *cli_credentials_get_password(struct cli_credentials *cred)
171 {
172         if (cred->machine_account_pending) {
173                 cli_credentials_set_machine_account(cred);
174         }
175
176         if (cred->password_obtained == CRED_CALLBACK) {
177                 cred->password = cred->password_cb(cred);
178                 cred->password_obtained = CRED_SPECIFIED;
179         }
180
181         return cred->password;
182 }
183
184 BOOL cli_credentials_set_password(struct cli_credentials *cred, 
185                                   const char *val, 
186                                   enum credentials_obtained obtained)
187 {
188         if (obtained >= cred->password_obtained) {
189                 cred->password = talloc_strdup(cred, val);
190                 cred->password_obtained = obtained;
191
192                 cred->nt_hash = NULL;
193                 return True;
194         }
195
196         return False;
197 }
198
199 BOOL cli_credentials_set_password_callback(struct cli_credentials *cred,
200                                            const char *(*password_cb) (struct cli_credentials *))
201 {
202         if (cred->password_obtained < CRED_CALLBACK) {
203                 cred->password_cb = password_cb;
204                 cred->password_obtained = CRED_CALLBACK;
205                 return True;
206         }
207
208         return False;
209 }
210
211 /**
212  * Obtain the password for this credentials context.
213  * @param cred credentials context
214  * @retval If set, the cleartext password, otherwise NULL
215  */
216 const struct samr_Password *cli_credentials_get_nt_hash(struct cli_credentials *cred, 
217                                                         TALLOC_CTX *mem_ctx)
218 {
219         const char *password = cli_credentials_get_password(cred);
220
221         if (password) {
222                 struct samr_Password *nt_hash = talloc(mem_ctx, struct samr_Password);
223                 if (!nt_hash) {
224                         return NULL;
225                 }
226                 
227                 E_md4hash(password, nt_hash->hash);    
228
229                 return nt_hash;
230         } else {
231                 return cred->nt_hash;
232         }
233 }
234
235 BOOL cli_credentials_set_nt_hash(struct cli_credentials *cred,
236                                  const struct samr_Password *nt_hash, 
237                                  enum credentials_obtained obtained)
238 {
239         if (obtained >= cred->password_obtained) {
240                 cli_credentials_set_password(cred, NULL, obtained);
241                 cred->nt_hash = talloc(cred, struct samr_Password);
242                 *cred->nt_hash = *nt_hash;
243                 return True;
244         }
245
246         return False;
247 }
248
249 int cli_credentials_set_from_ccache(struct cli_credentials *cred, 
250                                     enum credentials_obtained obtained)
251 {
252         
253         krb5_principal princ;
254         krb5_error_code ret;
255         char *name;
256         char **realm;
257
258         ret = krb5_cc_get_principal(cred->ccache->smb_krb5_context->krb5_context, 
259                                     cred->ccache->ccache, &princ);
260
261         if (ret) {
262                 char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred);
263                 DEBUG(1,("failed to get principal from ccache: %s\n", 
264                          err_mess));
265                 talloc_free(err_mess);
266                 return ret;
267         }
268         
269         ret = krb5_unparse_name(cred->ccache->smb_krb5_context->krb5_context, princ, &name);
270         if (ret) {
271                 char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred);
272                 DEBUG(1,("failed to unparse principal from ccache: %s\n", 
273                          err_mess));
274                 talloc_free(err_mess);
275                 return ret;
276         }
277
278         realm = krb5_princ_realm(cred->ccache->smb_krb5_context->krb5_context, princ);
279
280         cli_credentials_set_realm(cred, *realm, obtained);
281         cli_credentials_set_principal(cred, name, obtained);
282
283         free(name);
284
285         krb5_free_principal(cred->ccache->smb_krb5_context->krb5_context, princ);
286
287         cred->ccache_obtained = obtained;
288
289         return 0;
290 }
291
292
293 static int free_mccache(void *ptr) {
294         struct ccache_container *ccc = ptr;
295         krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
296
297         return 0;
298 }
299
300 static int free_dccache(void *ptr) {
301         struct ccache_container *ccc = ptr;
302         krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
303
304         return 0;
305 }
306
307 static int cli_credentials_set_ccache(struct cli_credentials *cred, 
308                                       const char *name, 
309                                       enum credentials_obtained obtained)
310 {
311         krb5_error_code ret;
312         krb5_principal princ;
313         struct ccache_container *ccc = talloc(cred, struct ccache_container);
314         if (!ccc) {
315                 return ENOMEM;
316         }
317
318         ret = smb_krb5_init_context(ccc, &ccc->smb_krb5_context);
319         if (ret) {
320                 talloc_free(ccc);
321                 return ret;
322         }
323         if (name) {
324                 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
325                 if (ret) {
326                         DEBUG(1,("failed to read krb5 ccache: %s: %s\n", 
327                                  name, 
328                                  smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
329                         talloc_free(ccc);
330                         return ret;
331                 }
332         } else {
333                 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
334                 if (ret) {
335                         DEBUG(1,("failed to read default krb5 ccache: %s\n", 
336                                  smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
337                         talloc_free(ccc);
338                         return ret;
339                 }
340         }
341
342         talloc_set_destructor(ccc, free_dccache);
343
344         ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
345
346         if (ret) {
347                 DEBUG(1,("failed to get principal from default ccache: %s\n", 
348                          smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
349                 talloc_free(ccc);               
350                 return ret;
351         }
352
353         krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
354
355         cred->ccache = ccc;
356         talloc_steal(cred, ccc);
357
358         ret = cli_credentials_set_from_ccache(cred, obtained);
359
360         if (ret) {
361                 return ret;
362         }
363
364         return 0;
365 }
366
367
368 int cli_credentials_new_ccache(struct cli_credentials *cred)
369 {
370         krb5_error_code ret;
371         char *rand_string;
372         struct ccache_container *ccc = talloc(cred, struct ccache_container);
373         char *ccache_name;
374         if (!ccc) {
375                 return ENOMEM;
376         }
377
378         rand_string = generate_random_str(NULL, 16);
379         if (!rand_string) {
380                 talloc_free(ccc);
381                 return ENOMEM;
382         }
383
384         ccache_name = talloc_asprintf(ccc, "MEMORY:%s", 
385                               rand_string);
386         talloc_free(rand_string);
387
388         if (!ccache_name) {
389                 talloc_free(ccc);
390                 return ENOMEM;
391         }
392
393         ret = smb_krb5_init_context(ccc, &ccc->smb_krb5_context);
394         if (ret) {
395                 talloc_free(ccache_name);
396                 talloc_free(ccc);
397                 return ret;
398         }
399
400         ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, &ccc->ccache);
401         if (ret) {
402                 DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n", 
403                          ccache_name,
404                          smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
405                 talloc_free(ccache_name);
406                 talloc_free(ccc);
407                 return ret;
408         }
409
410         talloc_set_destructor(ccc, free_mccache);
411
412         cred->ccache = ccc;
413         talloc_steal(cred, ccc);
414         talloc_free(ccache_name);
415
416         return ret;
417 }
418
419 int cli_credentials_get_ccache(struct cli_credentials *cred, 
420                                struct ccache_container **ccc)
421 {
422         krb5_error_code ret;
423         
424         if (cred->ccache_obtained >= (MAX(cred->principal_obtained, 
425                                           cred->username_obtained))) {
426                 *ccc = cred->ccache;
427                 return 0;
428         }
429         if (cli_credentials_is_anonymous(cred)) {
430                 return EINVAL;
431         }
432
433         ret = cli_credentials_new_ccache(cred);
434         if (ret) {
435                 return ret;
436         }
437         ret = kinit_to_ccache(cred, cred, cred->ccache->smb_krb5_context, cred->ccache->ccache);
438         if (ret) {
439                 return ret;
440         }
441         ret = cli_credentials_set_from_ccache(cred, cred->principal_obtained);
442
443         if (ret) {
444                 return ret;
445         }
446         *ccc = cred->ccache;
447         return ret;
448 }
449
450
451 /**
452  * Obtain the 'short' or 'NetBIOS' domain for this credentials context.
453  * @param cred credentials context
454  * @retval The domain set on this context. 
455  * @note Return value will never be NULL except by programmer error.
456  */
457 const char *cli_credentials_get_domain(struct cli_credentials *cred)
458 {
459         if (cred->machine_account_pending) {
460                 cli_credentials_set_machine_account(cred);
461         }
462
463         /* If we have a principal set on this, we want to login with "" domain and user@realm */
464         if (cred->domain_obtained < cred->principal_obtained) {
465                 return "";
466         }
467
468         if (cred->domain_obtained == CRED_CALLBACK) {
469                 cred->domain = cred->domain_cb(cred);
470                 cred->domain_obtained = CRED_SPECIFIED;
471         }
472
473         return cred->domain;
474 }
475
476
477 BOOL cli_credentials_set_domain(struct cli_credentials *cred, 
478                                 const char *val, 
479                                 enum credentials_obtained obtained)
480 {
481         if (obtained >= cred->domain_obtained) {
482                 cred->domain = talloc_strdup(cred, val);
483                 cred->domain_obtained = obtained;
484                 return True;
485         }
486
487         return False;
488 }
489
490 BOOL cli_credentials_set_domain_callback(struct cli_credentials *cred,
491                                          const char *(*domain_cb) (struct cli_credentials *))
492 {
493         if (cred->domain_obtained < CRED_CALLBACK) {
494                 cred->domain_cb = domain_cb;
495                 cred->domain_obtained = CRED_CALLBACK;
496                 return True;
497         }
498
499         return False;
500 }
501
502 /**
503  * Obtain the Kerberos realm for this credentials context.
504  * @param cred credentials context
505  * @retval The realm set on this context. 
506  * @note Return value will never be NULL except by programmer error.
507  */
508 const char *cli_credentials_get_realm(struct cli_credentials *cred)
509 {       
510         if (cred->machine_account_pending) {
511                 cli_credentials_set_machine_account(cred);
512         }
513
514         if (cred->realm_obtained == CRED_CALLBACK) {
515                 cred->realm = cred->realm_cb(cred);
516                 cred->realm_obtained = CRED_SPECIFIED;
517         }
518
519         return cred->realm;
520 }
521
522 /**
523  * Set the realm for this credentials context, and force it to
524  * uppercase for the sainity of our local kerberos libraries 
525  */
526 BOOL cli_credentials_set_realm(struct cli_credentials *cred, 
527                                const char *val, 
528                                enum credentials_obtained obtained)
529 {
530         if (obtained >= cred->realm_obtained) {
531                 cred->realm = strupper_talloc(cred, val);
532                 cred->realm_obtained = obtained;
533                 return True;
534         }
535
536         return False;
537 }
538
539 BOOL cli_credentials_set_realm_callback(struct cli_credentials *cred,
540                                         const char *(*realm_cb) (struct cli_credentials *))
541 {
542         if (cred->realm_obtained < CRED_CALLBACK) {
543                 cred->realm_cb = realm_cb;
544                 cred->realm_obtained = CRED_CALLBACK;
545                 return True;
546         }
547
548         return False;
549 }
550
551 /**
552  * Obtain the 'short' or 'NetBIOS' workstation name for this credentials context.
553  *
554  * @param cred credentials context
555  * @retval The workstation name set on this context. 
556  * @note Return value will never be NULL except by programmer error.
557  */
558 const char *cli_credentials_get_workstation(struct cli_credentials *cred)
559 {
560         if (cred->workstation_obtained == CRED_CALLBACK) {
561                 cred->workstation = cred->workstation_cb(cred);
562                 cred->workstation_obtained = CRED_SPECIFIED;
563         }
564
565         return cred->workstation;
566 }
567
568 BOOL cli_credentials_set_workstation(struct cli_credentials *cred, 
569                                      const char *val, 
570                                      enum credentials_obtained obtained)
571 {
572         if (obtained >= cred->workstation_obtained) {
573                 cred->workstation = talloc_strdup(cred, val);
574                 cred->workstation_obtained = obtained;
575                 return True;
576         }
577
578         return False;
579 }
580
581 BOOL cli_credentials_set_workstation_callback(struct cli_credentials *cred,
582                                               const char *(*workstation_cb) (struct cli_credentials *))
583 {
584         if (cred->workstation_obtained < CRED_CALLBACK) {
585                 cred->workstation_cb = workstation_cb;
586                 cred->workstation_obtained = CRED_CALLBACK;
587                 return True;
588         }
589
590         return False;
591 }
592
593 /**
594  * Read a file descriptor, and parse it for a password (eg from a file or stdin)
595  *
596  * @param credentials Credentials structure on which to set the password
597  * @param fd open file descriptor to read the password from 
598  * @param obtained This enum describes how 'specified' this password is
599  */
600
601 BOOL cli_credentials_parse_password_fd(struct cli_credentials *credentials, 
602                                        int fd, enum credentials_obtained obtained)
603 {
604         char *p;
605         char pass[128];
606
607         for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
608                 p && p - pass < sizeof(pass);) {
609                 switch (read(fd, p, 1)) {
610                 case 1:
611                         if (*p != '\n' && *p != '\0') {
612                                 *++p = '\0'; /* advance p, and null-terminate pass */
613                                 break;
614                         }
615                 case 0:
616                         if (p - pass) {
617                                 *p = '\0'; /* null-terminate it, just in case... */
618                                 p = NULL; /* then force the loop condition to become false */
619                                 break;
620                         } else {
621                                 fprintf(stderr, "Error reading password from file descriptor %d: %s\n", fd, "empty password\n");
622                                 return False;
623                         }
624
625                 default:
626                         fprintf(stderr, "Error reading password from file descriptor %d: %s\n",
627                                         fd, strerror(errno));
628                         return False;
629                 }
630         }
631
632         cli_credentials_set_password(credentials, pass, obtained);
633         return True;
634 }
635
636 /**
637  * Read a named file, and parse it for a password
638  *
639  * @param credentials Credentials structure on which to set the password
640  * @param file a named file to read the password from 
641  * @param obtained This enum describes how 'specified' this password is
642  */
643
644 BOOL cli_credentials_parse_password_file(struct cli_credentials *credentials, const char *file, enum credentials_obtained obtained)
645 {
646         int fd = open(file, O_RDONLY, 0);
647         BOOL ret;
648
649         if (fd < 0) {
650                 fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
651                                 file, strerror(errno));
652                 return False;
653         }
654
655         ret = cli_credentials_parse_password_fd(credentials, fd, obtained);
656
657         close(fd);
658         
659         return ret;
660 }
661
662 /**
663  * Read a named file, and parse it for username, domain, realm and password
664  *
665  * @param credentials Credentials structure on which to set the password
666  * @param file a named file to read the details from 
667  * @param obtained This enum describes how 'specified' this password is
668  */
669
670 BOOL cli_credentials_parse_file(struct cli_credentials *cred, const char *file, enum credentials_obtained obtained) 
671 {
672         XFILE *auth;
673         char buf[128];
674         uint16_t len = 0;
675         char *ptr, *val, *param;
676
677         if ((auth=x_fopen(file, O_RDONLY, 0)) == NULL)
678         {
679                 /* fail if we can't open the credentials file */
680                 d_printf("ERROR: Unable to open credentials file!\n");
681                 return False;
682         }
683
684         while (!x_feof(auth))
685         {
686                 /* get a line from the file */
687                 if (!x_fgets(buf, sizeof(buf), auth))
688                         continue;
689                 len = strlen(buf);
690
691                 if ((len) && (buf[len-1]=='\n'))
692                 {
693                         buf[len-1] = '\0';
694                         len--;
695                 }
696                 if (len == 0)
697                         continue;
698
699                 /* break up the line into parameter & value.
700                  * will need to eat a little whitespace possibly */
701                 param = buf;
702                 if (!(ptr = strchr_m (buf, '=')))
703                         continue;
704
705                 val = ptr+1;
706                 *ptr = '\0';
707
708                 /* eat leading white space */
709                 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
710                         val++;
711
712                 if (strwicmp("password", param) == 0) {
713                         cli_credentials_set_password(cred, val, obtained);
714                 } else if (strwicmp("username", param) == 0) {
715                         cli_credentials_set_username(cred, val, obtained);
716                 } else if (strwicmp("domain", param) == 0) {
717                         cli_credentials_set_domain(cred, val, obtained);
718                 } else if (strwicmp("realm", param) == 0) {
719                         cli_credentials_set_realm(cred, val, obtained);
720                 }
721                 memset(buf, 0, sizeof(buf));
722         }
723
724         x_fclose(auth);
725         return True;
726 }
727
728
729 /**
730  * Given a string, typically obtained from a -U argument, parse it into domain, username, realm and password fields
731  *
732  * The format accepted is [domain\\]user[%password] or user[@realm][%password]
733  *
734  * @param credentials Credentials structure on which to set the password
735  * @param data the string containing the username, password etc
736  * @param obtained This enum describes how 'specified' this password is
737  */
738
739 void cli_credentials_parse_string(struct cli_credentials *credentials, const char *data, enum credentials_obtained obtained)
740 {
741         char *uname, *p;
742
743         if (strcmp("%",data) == 0) {
744                 cli_credentials_set_anonymous(credentials);
745                 return;
746         }
747
748         uname = talloc_strdup(credentials, data); 
749         if ((p = strchr_m(uname,'%'))) {
750                 *p = 0;
751                 cli_credentials_set_password(credentials, p+1, obtained);
752         }
753
754         if ((p = strchr_m(uname,'@'))) {
755                 cli_credentials_set_principal(credentials, uname, obtained);
756                 *p = 0;
757                 cli_credentials_set_realm(credentials, p+1, obtained);
758                 return;
759         } else if ((p = strchr_m(uname,'\\')) || (p = strchr_m(uname, '/'))) {
760                 *p = 0;
761                 cli_credentials_set_domain(credentials, uname, obtained);
762                 uname = p+1;
763         }
764         cli_credentials_set_username(credentials, uname, obtained);
765 }
766
767 /**
768  * Specifies default values for domain, workstation and realm
769  * from the smb.conf configuration file
770  *
771  * @param cred Credentials structure to fill in
772  */
773 void cli_credentials_set_conf(struct cli_credentials *cred)
774 {
775         cli_credentials_set_username(cred, "", CRED_UNINITIALISED);
776         cli_credentials_set_domain(cred, lp_workgroup(), CRED_UNINITIALISED);
777         cli_credentials_set_workstation(cred, lp_netbios_name(), CRED_UNINITIALISED);
778         cli_credentials_set_realm(cred, lp_realm(), CRED_UNINITIALISED);
779 }
780
781 /**
782  * Guess defaults for credentials from environment variables, 
783  * and from the configuration file
784  * 
785  * @param cred Credentials structure to fill in
786  */
787 void cli_credentials_guess(struct cli_credentials *cred)
788 {
789         char *p;
790
791         cli_credentials_set_conf(cred);
792         
793         if (getenv("LOGNAME")) {
794                 cli_credentials_set_username(cred, getenv("LOGNAME"), CRED_GUESS_ENV);
795         }
796
797         if (getenv("USER")) {
798                 cli_credentials_parse_string(cred, getenv("USER"), CRED_GUESS_ENV);
799                 if ((p = strchr_m(getenv("USER"),'%'))) {
800                         memset(p,0,strlen(cred->password));
801                 }
802         }
803
804         if (getenv("DOMAIN")) {
805                 cli_credentials_set_domain(cred, getenv("DOMAIN"), CRED_GUESS_ENV);
806         }
807
808         if (getenv("PASSWD")) {
809                 cli_credentials_set_password(cred, getenv("PASSWD"), CRED_GUESS_ENV);
810         }
811
812         if (getenv("PASSWD_FD")) {
813                 cli_credentials_parse_password_fd(cred, atoi(getenv("PASSWD_FD")), CRED_GUESS_FILE);
814         }
815         
816         if (getenv("PASSWD_FILE")) {
817                 cli_credentials_parse_password_file(cred, getenv("PASSWD_FILE"), CRED_GUESS_FILE);
818         }
819
820         cli_credentials_set_ccache(cred, NULL, CRED_GUESS_FILE);
821 }
822
823 /**
824  * Fill in credentials for the machine trust account, from the secrets database.
825  * 
826  * @param cred Credentials structure to fill in
827  * @retval NTSTATUS error detailing any failure
828  */
829 NTSTATUS cli_credentials_set_machine_account(struct cli_credentials *cred)
830 {
831         TALLOC_CTX *mem_ctx;
832         
833         struct ldb_context *ldb;
834         int ldb_ret;
835         struct ldb_message **msgs;
836         const char *attrs[] = {
837                 "secret",
838                 "samAccountName",
839                 "flatname",
840                 "realm",
841                 "secureChannelType",
842                 "ntPwdHash",
843                 "msDS-KeyVersionNumber",
844                 NULL
845         };
846         
847         const char *machine_account;
848         const char *password;
849         const char *domain;
850         const char *realm;
851         enum netr_SchannelType sct;
852         
853         /* ok, we are going to get it now, don't recurse back here */
854         cred->machine_account_pending = False;
855
856         mem_ctx = talloc_named(cred, 0, "cli_credentials fetch machine password");
857         /* Local secrets are stored in secrets.ldb */
858         ldb = secrets_db_connect(mem_ctx);
859         if (!ldb) {
860                 DEBUG(1, ("Could not open secrets.ldb\n"));
861                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
862         }
863
864         /* search for the secret record */
865         ldb_ret = gendb_search(ldb,
866                                mem_ctx, ldb_dn_explode(mem_ctx, SECRETS_PRIMARY_DOMAIN_DN), 
867                                &msgs, attrs,
868                                SECRETS_PRIMARY_DOMAIN_FILTER, 
869                                cli_credentials_get_domain(cred));
870         if (ldb_ret == 0) {
871                 DEBUG(1, ("Could not find join record to domain: %s\n",
872                           cli_credentials_get_domain(cred)));
873                 talloc_free(mem_ctx);
874                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
875         } else if (ldb_ret != 1) {
876                 DEBUG(1, ("Found more than one (%d) join records to domain: %s\n",
877                           ldb_ret, cli_credentials_get_domain(cred)));
878                 talloc_free(mem_ctx);
879                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
880         }
881         
882         password = ldb_msg_find_string(msgs[0], "secret", NULL);
883
884         machine_account = ldb_msg_find_string(msgs[0], "samAccountName", NULL);
885
886         if (!machine_account) {
887                 DEBUG(1, ("Could not find 'samAccountName' in join record to domain: %s\n",
888                           cli_credentials_get_domain(cred)));
889                 talloc_free(mem_ctx);
890                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
891         }
892         
893         sct = ldb_msg_find_int(msgs[0], "secureChannelType", 0);
894         if (!sct) { 
895                 DEBUG(1, ("Domain join for acocunt %s did not have a secureChannelType set!\n",
896                           machine_account));
897                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
898         }
899         
900         if (!password) {
901                 const struct ldb_val *nt_password_hash = ldb_msg_find_ldb_val(msgs[0], "ntPwdHash");
902                 struct samr_Password hash;
903                 ZERO_STRUCT(hash);
904                 if (nt_password_hash) {
905                         memcpy(hash.hash, nt_password_hash->data, 
906                                MIN(nt_password_hash->length, sizeof(hash.hash)));
907                 
908                         cli_credentials_set_nt_hash(cred, &hash, CRED_SPECIFIED);
909                 } else {
910                 
911                         DEBUG(1, ("Could not find 'secret' in join record to domain: %s\n",
912                                   cli_credentials_get_domain(cred)));
913                         talloc_free(mem_ctx);
914                         return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
915                 }
916         }
917         
918         cli_credentials_set_secure_channel_type(cred, sct);
919
920         domain = ldb_msg_find_string(msgs[0], "flatname", NULL);
921         if (domain) {
922                 cli_credentials_set_domain(cred, domain, CRED_SPECIFIED);
923         }
924
925         realm = ldb_msg_find_string(msgs[0], "realm", NULL);
926         if (realm) {
927                 cli_credentials_set_realm(cred, realm, CRED_SPECIFIED);
928         }
929
930         cli_credentials_set_username(cred, machine_account, CRED_SPECIFIED);
931         if (password) {
932                 cli_credentials_set_password(cred, password, CRED_SPECIFIED);
933         }
934
935         cli_credentials_set_kvno(cred, ldb_msg_find_int(msgs[0], "msDS-KeyVersionNumber", 0));
936         
937         talloc_free(mem_ctx);
938         
939         return NT_STATUS_OK;
940 }
941
942 /**
943  * Ask that when required, the credentials system will be filled with
944  * machine trust account, from the secrets database.
945  * 
946  * @param cred Credentials structure to fill in
947  * @note This function is used to call the above function after, rather 
948  *       than during, popt processing.
949  *
950  */
951 void cli_credentials_set_machine_account_pending(struct cli_credentials *cred)
952 {
953         cred->machine_account_pending = True;
954 }
955
956 /**
957  * Attach NETLOGON credentials for use with SCHANNEL
958  */
959
960 void cli_credentials_set_netlogon_creds(struct cli_credentials *cred, 
961                                         struct creds_CredentialState *netlogon_creds)
962 {
963         cred->netlogon_creds = talloc_reference(cred, netlogon_creds);
964 }
965
966 /**
967  * Return attached NETLOGON credentials 
968  */
969
970 struct creds_CredentialState *cli_credentials_get_netlogon_creds(struct cli_credentials *cred)
971 {
972         return cred->netlogon_creds;
973 }
974
975 /** 
976  * Set NETLOGON secure channel type
977  */
978
979 void cli_credentials_set_secure_channel_type(struct cli_credentials *cred,
980                                              enum netr_SchannelType secure_channel_type)
981 {
982         cred->secure_channel_type = secure_channel_type;
983 }
984
985 /**
986  * Return NETLOGON secure chanel type
987  */
988
989 enum netr_SchannelType cli_credentials_get_secure_channel_type(struct cli_credentials *cred)
990 {
991         return cred->secure_channel_type;
992 }
993
994 /** 
995  * Set Kerberos KVNO
996  */
997
998 void cli_credentials_set_kvno(struct cli_credentials *cred,
999                               int kvno)
1000 {
1001         cred->kvno = kvno;
1002 }
1003
1004 /**
1005  * Return Kerberos KVNO
1006  */
1007
1008 int cli_credentials_get_kvno(struct cli_credentials *cred)
1009 {
1010         return cred->kvno;
1011 }
1012
1013 /**
1014  * Fill in a credentials structure as the anonymous user
1015  */
1016 void cli_credentials_set_anonymous(struct cli_credentials *cred) 
1017 {
1018         cli_credentials_set_username(cred, "", CRED_SPECIFIED);
1019         cli_credentials_set_domain(cred, "", CRED_SPECIFIED);
1020         cli_credentials_set_password(cred, NULL, CRED_SPECIFIED);
1021 }
1022
1023 /**
1024  * Describe a credentials context as anonymous or authenticated
1025  * @retval True if anonymous, False if a username is specified
1026  */
1027
1028 BOOL cli_credentials_is_anonymous(struct cli_credentials *cred)
1029 {
1030         TALLOC_CTX *tmp_ctx = talloc_new(cred);
1031         const char *username = cli_credentials_get_username(cred, tmp_ctx);
1032         
1033         /* Yes, it is deliberate that we die if we have a NULL pointer
1034          * here - anonymous is "", not NULL, which is 'never specified,
1035          * never guessed', ie programmer bug */
1036         if (!username[0]) {
1037                 talloc_free(tmp_ctx);
1038                 return True;
1039         }
1040         
1041         talloc_free(tmp_ctx);
1042         return False;
1043 }