r15422: Fix issues with functions being called recursively in the credentials
[ira/wip.git] / source4 / auth / credentials / credentials.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    User credentials handling
5
6    Copyright (C) Jelmer Vernooij 2005
7    Copyright (C) Tim Potter 2001
8    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26 #include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
27 #include "auth/gensec/gensec.h"
28 #include "libcli/auth/libcli_auth.h"
29
30 /**
31  * Create a new credentials structure
32  * @param mem_ctx TALLOC_CTX parent for credentials structure 
33  */
34 struct cli_credentials *cli_credentials_init(TALLOC_CTX *mem_ctx) 
35 {
36         struct cli_credentials *cred = talloc(mem_ctx, struct cli_credentials);
37         if (!cred) {
38                 return cred;
39         }
40
41         cred->netlogon_creds = NULL;
42         cred->machine_account_pending = False;
43         cred->workstation_obtained = CRED_UNINITIALISED;
44         cred->username_obtained = CRED_UNINITIALISED;
45         cred->password_obtained = CRED_UNINITIALISED;
46         cred->domain_obtained = CRED_UNINITIALISED;
47         cred->realm_obtained = CRED_UNINITIALISED;
48         cred->ccache_obtained = CRED_UNINITIALISED;
49         cred->client_gss_creds_obtained = CRED_UNINITIALISED;
50         cred->server_gss_creds_obtained = CRED_UNINITIALISED;
51         cred->keytab_obtained = CRED_UNINITIALISED;
52         cred->principal_obtained = CRED_UNINITIALISED;
53
54         cred->old_password = NULL;
55         cred->smb_krb5_context = NULL;
56         cred->salt_principal = NULL;
57         cred->machine_account = False;
58
59         cred->bind_dn = NULL;
60
61         cred->tries = 3;
62
63         cli_credentials_set_kerberos_state(cred, CRED_AUTO_USE_KERBEROS);
64
65         return cred;
66 }
67
68 void cli_credentials_set_kerberos_state(struct cli_credentials *creds, 
69                                         enum credentials_use_kerberos use_kerberos)
70 {
71         creds->use_kerberos = use_kerberos;
72 }
73
74 enum credentials_use_kerberos cli_credentials_get_kerberos_state(struct cli_credentials *creds)
75 {
76         return creds->use_kerberos;
77 }
78
79
80 /**
81  * Obtain the username for this credentials context.
82  * @param cred credentials context
83  * @retval The username set on this context.
84  * @note Return value will never be NULL except by programmer error.
85  */
86 const char *cli_credentials_get_username(struct cli_credentials *cred)
87 {
88         if (cred->machine_account_pending) {
89                 cli_credentials_set_machine_account(cred);
90         }
91
92         if (cred->username_obtained == CRED_CALLBACK && 
93             !cred->callback_running) {
94                 cred->callback_running = True;
95                 cred->username = cred->username_cb(cred);
96                 cred->callback_running = False;
97                 cred->username_obtained = CRED_SPECIFIED;
98         }
99
100         return cred->username;
101 }
102
103 BOOL cli_credentials_set_username(struct cli_credentials *cred, 
104                                   const char *val, enum credentials_obtained obtained)
105 {
106         if (obtained >= cred->username_obtained) {
107                 cred->username = talloc_strdup(cred, val);
108                 cred->username_obtained = obtained;
109                 return True;
110         }
111
112         return False;
113 }
114
115 BOOL cli_credentials_set_username_callback(struct cli_credentials *cred,
116                                   const char *(*username_cb) (struct cli_credentials *))
117 {
118         if (cred->username_obtained < CRED_CALLBACK) {
119                 cred->username_cb = username_cb;
120                 cred->username_obtained = CRED_CALLBACK;
121                 return True;
122         }
123
124         return False;
125 }
126
127 BOOL cli_credentials_set_bind_dn(struct cli_credentials *cred, 
128                                  const char *bind_dn)
129 {
130         cred->bind_dn = talloc_strdup(cred, bind_dn);
131         return True;
132 }
133
134 /**
135  * Obtain the BIND DN for this credentials context.
136  * @param cred credentials context
137  * @retval The username set on this context.
138  * @note Return value will be NULL if not specified explictly
139  */
140 const char *cli_credentials_get_bind_dn(struct cli_credentials *cred)
141 {
142         return cred->bind_dn;
143 }
144
145
146 /**
147  * Obtain the client principal for this credentials context.
148  * @param cred credentials context
149  * @retval The username set on this context.
150  * @note Return value will never be NULL except by programmer error.
151  */
152 const char *cli_credentials_get_principal(struct cli_credentials *cred, TALLOC_CTX *mem_ctx)
153 {
154         if (cred->machine_account_pending) {
155                 cli_credentials_set_machine_account(cred);
156         }
157
158         if (cred->principal_obtained == CRED_CALLBACK && 
159             !cred->callback_running) {
160                 cred->callback_running = False;
161                 cred->principal = cred->principal_cb(cred);
162                 cred->callback_running = True;
163                 cred->principal_obtained = CRED_SPECIFIED;
164         }
165
166         if (cred->principal_obtained < cred->username_obtained) {
167                 if (cred->domain_obtained > cred->realm_obtained) {
168                         return talloc_asprintf(mem_ctx, "%s@%s", 
169                                                cli_credentials_get_username(cred),
170                                                cli_credentials_get_domain(cred));
171                 } else {
172                         return talloc_asprintf(mem_ctx, "%s@%s", 
173                                                cli_credentials_get_username(cred),
174                                                cli_credentials_get_realm(cred));
175                 }
176         }
177         return talloc_reference(mem_ctx, cred->principal);
178 }
179
180 BOOL cli_credentials_set_principal(struct cli_credentials *cred, 
181                                    const char *val, 
182                                    enum credentials_obtained obtained)
183 {
184         if (obtained >= cred->principal_obtained) {
185                 cred->principal = talloc_strdup(cred, val);
186                 cred->principal_obtained = obtained;
187                 return True;
188         }
189
190         return False;
191 }
192
193 /* Set a callback to get the principal.  This could be a popup dialog,
194  * a terminal prompt or similar.  */
195
196 BOOL cli_credentials_set_principal_callback(struct cli_credentials *cred,
197                                   const char *(*principal_cb) (struct cli_credentials *))
198 {
199         if (cred->principal_obtained < CRED_CALLBACK) {
200                 cred->principal_cb = principal_cb;
201                 cred->principal_obtained = CRED_CALLBACK;
202                 return True;
203         }
204
205         return False;
206 }
207
208 /* Some of our tools are 'anonymous by default'.  This is a single
209  * function to determine if authentication has been explicitly
210  * requested */
211
212 BOOL cli_credentials_authentication_requested(struct cli_credentials *cred) 
213 {
214         if (cred->bind_dn) {
215                 return True;
216         }
217
218         if (cred->machine_account_pending) {
219                 cli_credentials_set_machine_account(cred);
220         }
221
222         if (cred->principal_obtained >= CRED_SPECIFIED) {
223                 return True;
224         }
225         if (cred->username_obtained >= CRED_SPECIFIED) {
226                 return True;
227         }
228         return False;
229 }
230
231 /**
232  * Obtain the password for this credentials context.
233  * @param cred credentials context
234  * @retval If set, the cleartext password, otherwise NULL
235  */
236 const char *cli_credentials_get_password(struct cli_credentials *cred)
237 {
238         if (cred->machine_account_pending) {
239                 cli_credentials_set_machine_account(cred);
240         }
241
242         if (cred->password_obtained == CRED_CALLBACK && 
243             !cred->callback_running) {
244                 cred->callback_running = False;
245                 cred->password = cred->password_cb(cred);
246                 cred->callback_running = True;
247                 cred->password_obtained = CRED_CALLBACK_RESULT;
248         }
249
250         return cred->password;
251 }
252
253 /* Set a password on the credentials context, including an indication
254  * of 'how' the password was obtained */
255
256 BOOL cli_credentials_set_password(struct cli_credentials *cred, 
257                                   const char *val, 
258                                   enum credentials_obtained obtained)
259 {
260         if (obtained >= cred->password_obtained) {
261                 cred->password = talloc_strdup(cred, val);
262                 cred->password_obtained = obtained;
263
264                 cred->nt_hash = NULL;
265                 return True;
266         }
267
268         return False;
269 }
270
271 BOOL cli_credentials_set_password_callback(struct cli_credentials *cred,
272                                            const char *(*password_cb) (struct cli_credentials *))
273 {
274         if (cred->password_obtained < CRED_CALLBACK) {
275                 cred->password_cb = password_cb;
276                 cred->password_obtained = CRED_CALLBACK;
277                 return True;
278         }
279
280         return False;
281 }
282
283 /**
284  * Obtain the 'old' password for this credentials context (used for join accounts).
285  * @param cred credentials context
286  * @retval If set, the cleartext password, otherwise NULL
287  */
288 const char *cli_credentials_get_old_password(struct cli_credentials *cred)
289 {
290         if (cred->machine_account_pending) {
291                 cli_credentials_set_machine_account(cred);
292         }
293
294         return cred->old_password;
295 }
296
297 BOOL cli_credentials_set_old_password(struct cli_credentials *cred, 
298                                       const char *val, 
299                                       enum credentials_obtained obtained)
300 {
301         cred->old_password = talloc_strdup(cred, val);
302         return True;
303 }
304
305 /**
306  * Obtain the password, in the form MD4(unicode(password)) for this credentials context.
307  *
308  * Sometimes we only have this much of the password, while the rest of
309  * the time this call avoids calling E_md4hash themselves.
310  *
311  * @param cred credentials context
312  * @retval If set, the cleartext password, otherwise NULL
313  */
314 const struct samr_Password *cli_credentials_get_nt_hash(struct cli_credentials *cred, 
315                                                         TALLOC_CTX *mem_ctx)
316 {
317         const char *password = cli_credentials_get_password(cred);
318
319         if (password) {
320                 struct samr_Password *nt_hash = talloc(mem_ctx, struct samr_Password);
321                 if (!nt_hash) {
322                         return NULL;
323                 }
324                 
325                 E_md4hash(password, nt_hash->hash);    
326
327                 return nt_hash;
328         } else {
329                 return cred->nt_hash;
330         }
331 }
332
333 BOOL cli_credentials_set_nt_hash(struct cli_credentials *cred,
334                                  const struct samr_Password *nt_hash, 
335                                  enum credentials_obtained obtained)
336 {
337         if (obtained >= cred->password_obtained) {
338                 cli_credentials_set_password(cred, NULL, obtained);
339                 cred->nt_hash = talloc(cred, struct samr_Password);
340                 *cred->nt_hash = *nt_hash;
341                 return True;
342         }
343
344         return False;
345 }
346
347 /**
348  * Obtain the 'short' or 'NetBIOS' domain for this credentials context.
349  * @param cred credentials context
350  * @retval The domain set on this context. 
351  * @note Return value will never be NULL except by programmer error.
352  */
353 const char *cli_credentials_get_domain(struct cli_credentials *cred)
354 {
355         if (cred->machine_account_pending) {
356                 cli_credentials_set_machine_account(cred);
357         }
358
359         if (cred->domain_obtained == CRED_CALLBACK && 
360             !cred->callback_running) {
361                 cred->callback_running = True;
362                 cred->domain = cred->domain_cb(cred);
363                 cred->callback_running = False;
364                 cred->domain_obtained = CRED_SPECIFIED;
365         }
366
367         return cred->domain;
368 }
369
370
371 BOOL cli_credentials_set_domain(struct cli_credentials *cred, 
372                                 const char *val, 
373                                 enum credentials_obtained obtained)
374 {
375         if (obtained >= cred->domain_obtained) {
376                 /* it is important that the domain be in upper case,
377                  * particularly for the sensitive NTLMv2
378                  * calculations */
379                 cred->domain = strupper_talloc(cred, val);
380                 cred->domain_obtained = obtained;
381                 return True;
382         }
383
384         return False;
385 }
386
387 BOOL cli_credentials_set_domain_callback(struct cli_credentials *cred,
388                                          const char *(*domain_cb) (struct cli_credentials *))
389 {
390         if (cred->domain_obtained < CRED_CALLBACK) {
391                 cred->domain_cb = domain_cb;
392                 cred->domain_obtained = CRED_CALLBACK;
393                 return True;
394         }
395
396         return False;
397 }
398
399 /**
400  * Obtain the Kerberos realm for this credentials context.
401  * @param cred credentials context
402  * @retval The realm set on this context. 
403  * @note Return value will never be NULL except by programmer error.
404  */
405 const char *cli_credentials_get_realm(struct cli_credentials *cred)
406 {       
407         if (cred->machine_account_pending) {
408                 cli_credentials_set_machine_account(cred);
409         }
410
411         if (cred->realm_obtained == CRED_CALLBACK && 
412             !cred->callback_running) {
413                 cred->callback_running = True;
414                 cred->realm = cred->realm_cb(cred);
415                 cred->callback_running = False;
416                 cred->realm_obtained = CRED_SPECIFIED;
417         }
418
419         return cred->realm;
420 }
421
422 /**
423  * Set the realm for this credentials context, and force it to
424  * uppercase for the sainity of our local kerberos libraries 
425  */
426 BOOL cli_credentials_set_realm(struct cli_credentials *cred, 
427                                const char *val, 
428                                enum credentials_obtained obtained)
429 {
430         if (obtained >= cred->realm_obtained) {
431                 cred->realm = strupper_talloc(cred, val);
432                 cred->realm_obtained = obtained;
433                 return True;
434         }
435
436         return False;
437 }
438
439 BOOL cli_credentials_set_realm_callback(struct cli_credentials *cred,
440                                         const char *(*realm_cb) (struct cli_credentials *))
441 {
442         if (cred->realm_obtained < CRED_CALLBACK) {
443                 cred->realm_cb = realm_cb;
444                 cred->realm_obtained = CRED_CALLBACK;
445                 return True;
446         }
447
448         return False;
449 }
450
451 /**
452  * Obtain the 'short' or 'NetBIOS' workstation name for this credentials context.
453  *
454  * @param cred credentials context
455  * @retval The workstation name set on this context. 
456  * @note Return value will never be NULL except by programmer error.
457  */
458 const char *cli_credentials_get_workstation(struct cli_credentials *cred)
459 {
460         if (cred->workstation_obtained == CRED_CALLBACK && 
461             !cred->callback_running) {
462                 cred->callback_running = True;
463                 cred->workstation = cred->workstation_cb(cred);
464                 cred->callback_running = False;
465                 cred->workstation_obtained = CRED_SPECIFIED;
466         }
467
468         return cred->workstation;
469 }
470
471 BOOL cli_credentials_set_workstation(struct cli_credentials *cred, 
472                                      const char *val, 
473                                      enum credentials_obtained obtained)
474 {
475         if (obtained >= cred->workstation_obtained) {
476                 cred->workstation = talloc_strdup(cred, val);
477                 cred->workstation_obtained = obtained;
478                 return True;
479         }
480
481         return False;
482 }
483
484 BOOL cli_credentials_set_workstation_callback(struct cli_credentials *cred,
485                                               const char *(*workstation_cb) (struct cli_credentials *))
486 {
487         if (cred->workstation_obtained < CRED_CALLBACK) {
488                 cred->workstation_cb = workstation_cb;
489                 cred->workstation_obtained = CRED_CALLBACK;
490                 return True;
491         }
492
493         return False;
494 }
495
496 /**
497  * Given a string, typically obtained from a -U argument, parse it into domain, username, realm and password fields
498  *
499  * The format accepted is [domain\\]user[%password] or user[@realm][%password]
500  *
501  * @param credentials Credentials structure on which to set the password
502  * @param data the string containing the username, password etc
503  * @param obtained This enum describes how 'specified' this password is
504  */
505
506 void cli_credentials_parse_string(struct cli_credentials *credentials, const char *data, enum credentials_obtained obtained)
507 {
508         char *uname, *p;
509
510         if (strcmp("%",data) == 0) {
511                 cli_credentials_set_anonymous(credentials);
512                 return;
513         }
514
515         uname = talloc_strdup(credentials, data); 
516         if ((p = strchr_m(uname,'%'))) {
517                 *p = 0;
518                 cli_credentials_set_password(credentials, p+1, obtained);
519         }
520
521         if ((p = strchr_m(uname,'@'))) {
522                 cli_credentials_set_principal(credentials, uname, obtained);
523                 *p = 0;
524                 cli_credentials_set_realm(credentials, p+1, obtained);
525                 return;
526         } else if ((p = strchr_m(uname,'\\')) || (p = strchr_m(uname, '/'))) {
527                 *p = 0;
528                 cli_credentials_set_domain(credentials, uname, obtained);
529                 uname = p+1;
530         }
531         cli_credentials_set_username(credentials, uname, obtained);
532 }
533
534 /**
535  * Given a a credentials structure, print it as a string
536  *
537  * The format output is [domain\\]user[%password] or user[@realm][%password]
538  *
539  * @param credentials Credentials structure on which to set the password
540  * @param mem_ctx The memory context to place the result on
541  */
542
543 const char *cli_credentials_get_unparsed_name(struct cli_credentials *credentials, TALLOC_CTX *mem_ctx)
544 {
545         const char *bind_dn = cli_credentials_get_bind_dn(credentials);
546         const char *domain;
547         const char *username;
548         const char *name;
549
550         if (bind_dn) {
551                 name = talloc_reference(mem_ctx, bind_dn);
552         } else {
553                 cli_credentials_get_ntlm_username_domain(credentials, mem_ctx, &username, &domain);
554                 if (domain && domain[0]) {
555                         name = talloc_asprintf(mem_ctx, "%s\\%s", 
556                                                domain, username);
557                 } else {
558                         name = talloc_asprintf(mem_ctx, "%s", 
559                                                username);
560                 }
561         }
562         return name;
563 }
564
565 /**
566  * Specifies default values for domain, workstation and realm
567  * from the smb.conf configuration file
568  *
569  * @param cred Credentials structure to fill in
570  */
571 void cli_credentials_set_conf(struct cli_credentials *cred)
572 {
573         cli_credentials_set_username(cred, "", CRED_UNINITIALISED);
574         cli_credentials_set_domain(cred, lp_workgroup(), CRED_UNINITIALISED);
575         cli_credentials_set_workstation(cred, lp_netbios_name(), CRED_UNINITIALISED);
576         cli_credentials_set_realm(cred, lp_realm(), CRED_UNINITIALISED);
577 }
578
579 /**
580  * Guess defaults for credentials from environment variables, 
581  * and from the configuration file
582  * 
583  * @param cred Credentials structure to fill in
584  */
585 void cli_credentials_guess(struct cli_credentials *cred)
586 {
587         char *p;
588
589         cli_credentials_set_conf(cred);
590         
591         if (getenv("LOGNAME")) {
592                 cli_credentials_set_username(cred, getenv("LOGNAME"), CRED_GUESS_ENV);
593         }
594
595         if (getenv("USER")) {
596                 cli_credentials_parse_string(cred, getenv("USER"), CRED_GUESS_ENV);
597                 if ((p = strchr_m(getenv("USER"),'%'))) {
598                         memset(p,0,strlen(cred->password));
599                 }
600         }
601
602         if (getenv("DOMAIN")) {
603                 cli_credentials_set_domain(cred, getenv("DOMAIN"), CRED_GUESS_ENV);
604         }
605
606         if (getenv("PASSWD")) {
607                 cli_credentials_set_password(cred, getenv("PASSWD"), CRED_GUESS_ENV);
608         }
609
610         if (getenv("PASSWD_FD")) {
611                 cli_credentials_parse_password_fd(cred, atoi(getenv("PASSWD_FD")), CRED_GUESS_FILE);
612         }
613         
614         if (getenv("PASSWD_FILE")) {
615                 cli_credentials_parse_password_file(cred, getenv("PASSWD_FILE"), CRED_GUESS_FILE);
616         }
617         
618         if (cli_credentials_get_kerberos_state(cred) != CRED_DONT_USE_KERBEROS) {
619                 cli_credentials_set_ccache(cred, NULL, CRED_GUESS_FILE);
620         }
621 }
622
623 /**
624  * Attach NETLOGON credentials for use with SCHANNEL
625  */
626
627 void cli_credentials_set_netlogon_creds(struct cli_credentials *cred, 
628                                         struct creds_CredentialState *netlogon_creds)
629 {
630         cred->netlogon_creds = talloc_reference(cred, netlogon_creds);
631 }
632
633 /**
634  * Return attached NETLOGON credentials 
635  */
636
637 struct creds_CredentialState *cli_credentials_get_netlogon_creds(struct cli_credentials *cred)
638 {
639         return cred->netlogon_creds;
640 }
641
642 /** 
643  * Set NETLOGON secure channel type
644  */
645
646 void cli_credentials_set_secure_channel_type(struct cli_credentials *cred,
647                                              enum netr_SchannelType secure_channel_type)
648 {
649         cred->secure_channel_type = secure_channel_type;
650 }
651
652 /**
653  * Return NETLOGON secure chanel type
654  */
655
656 enum netr_SchannelType cli_credentials_get_secure_channel_type(struct cli_credentials *cred)
657 {
658         return cred->secure_channel_type;
659 }
660
661 /**
662  * Fill in a credentials structure as the anonymous user
663  */
664 void cli_credentials_set_anonymous(struct cli_credentials *cred) 
665 {
666         cli_credentials_set_username(cred, "", CRED_SPECIFIED);
667         cli_credentials_set_domain(cred, "", CRED_SPECIFIED);
668         cli_credentials_set_password(cred, NULL, CRED_SPECIFIED);
669 }
670
671 /**
672  * Describe a credentials context as anonymous or authenticated
673  * @retval True if anonymous, False if a username is specified
674  */
675
676 BOOL cli_credentials_is_anonymous(struct cli_credentials *cred)
677 {
678         const char *username;
679         
680         if (cred->machine_account_pending) {
681                 cli_credentials_set_machine_account(cred);
682         }
683
684         username = cli_credentials_get_username(cred);
685         
686         /* Yes, it is deliberate that we die if we have a NULL pointer
687          * here - anonymous is "", not NULL, which is 'never specified,
688          * never guessed', ie programmer bug */
689         if (!username[0]) {
690                 return True;
691         }
692
693         return False;
694 }
695
696 /**
697  * Mark the current password for a credentials struct as wrong. This will 
698  * cause the password to be prompted again (if a callback is set).
699  *
700  * This will decrement the number of times the password can be tried.
701  *
702  * @retval whether the credentials struct is finished
703  */
704 BOOL cli_credentials_wrong_password(struct cli_credentials *cred)
705 {
706         if (cred->password_obtained != CRED_CALLBACK_RESULT) {
707                 return False;
708         }
709         
710         cred->password_obtained = CRED_CALLBACK;
711
712         cred->tries--;
713
714         return (cred->tries > 0);
715 }