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