r11995: A big kerberos-related update.
[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 "lib/ldb/include/ldb.h"
27 #include "librpc/gen_ndr/ndr_samr.h" /* for struct samrPassword */
28
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         return cred;
60 }
61
62 /**
63  * Obtain the username for this credentials context.
64  * @param cred credentials context
65  * @retval The username set on this context.
66  * @note Return value will never be NULL except by programmer error.
67  */
68 const char *cli_credentials_get_username(struct cli_credentials *cred)
69 {
70         if (cred->machine_account_pending) {
71                 cli_credentials_set_machine_account(cred);
72         }
73
74         if (cred->username_obtained == CRED_CALLBACK) {
75                 cred->username = cred->username_cb(cred);
76                 cred->username_obtained = CRED_SPECIFIED;
77         }
78
79         return cred->username;
80 }
81
82 BOOL cli_credentials_set_username(struct cli_credentials *cred, 
83                                   const char *val, enum credentials_obtained obtained)
84 {
85         if (obtained >= cred->username_obtained) {
86                 cred->username = talloc_strdup(cred, val);
87                 cred->username_obtained = obtained;
88                 return True;
89         }
90
91         return False;
92 }
93
94 BOOL cli_credentials_set_username_callback(struct cli_credentials *cred,
95                                   const char *(*username_cb) (struct cli_credentials *))
96 {
97         if (cred->username_obtained < CRED_CALLBACK) {
98                 cred->username_cb = username_cb;
99                 cred->username_obtained = CRED_CALLBACK;
100                 return True;
101         }
102
103         return False;
104 }
105
106
107
108 /**
109  * Obtain the client principal for this credentials context.
110  * @param cred credentials context
111  * @retval The username set on this context.
112  * @note Return value will never be NULL except by programmer error.
113  */
114 const char *cli_credentials_get_principal(struct cli_credentials *cred, TALLOC_CTX *mem_ctx)
115 {
116         if (cred->machine_account_pending) {
117                 cli_credentials_set_machine_account(cred);
118         }
119
120         if (cred->principal_obtained == CRED_CALLBACK) {
121                 cred->principal = cred->principal_cb(cred);
122                 cred->principal_obtained = CRED_SPECIFIED;
123         }
124
125         if (cred->principal_obtained < cred->username_obtained) {
126                 if (cred->domain_obtained > cred->realm_obtained) {
127                         return talloc_asprintf(mem_ctx, "%s@%s", 
128                                                cli_credentials_get_username(cred),
129                                                cli_credentials_get_domain(cred));
130                 } else {
131                         return talloc_asprintf(mem_ctx, "%s@%s", 
132                                                cli_credentials_get_username(cred),
133                                                cli_credentials_get_realm(cred));
134                 }
135         }
136         return talloc_reference(mem_ctx, cred->principal);
137 }
138
139 BOOL cli_credentials_set_principal(struct cli_credentials *cred, 
140                                    const char *val, 
141                                    enum credentials_obtained obtained)
142 {
143         if (obtained >= cred->principal_obtained) {
144                 cred->principal = talloc_strdup(cred, val);
145                 cred->principal_obtained = obtained;
146                 return True;
147         }
148
149         return False;
150 }
151
152 /* Set a callback to get the principal.  This could be a popup dialog,
153  * a terminal prompt or similar.  */
154
155 BOOL cli_credentials_set_principal_callback(struct cli_credentials *cred,
156                                   const char *(*principal_cb) (struct cli_credentials *))
157 {
158         if (cred->principal_obtained < CRED_CALLBACK) {
159                 cred->principal_cb = principal_cb;
160                 cred->principal_obtained = CRED_CALLBACK;
161                 return True;
162         }
163
164         return False;
165 }
166
167 /* Some of our tools are 'anonymous by default'.  This is a single
168  * function to determine if authentication has been explicitly
169  * requested */
170
171 BOOL cli_credentials_authentication_requested(struct cli_credentials *cred) 
172 {
173         if (cred->principal_obtained >= CRED_SPECIFIED) {
174                 return True;
175         }
176         if (cred->username_obtained >= CRED_SPECIFIED) {
177                 return True;
178         }
179         return False;
180 }
181
182 /**
183  * Obtain the password for this credentials context.
184  * @param cred credentials context
185  * @retval If set, the cleartext password, otherwise NULL
186  */
187 const char *cli_credentials_get_password(struct cli_credentials *cred)
188 {
189         if (cred->machine_account_pending) {
190                 cli_credentials_set_machine_account(cred);
191         }
192
193         if (cred->password_obtained == CRED_CALLBACK) {
194                 cred->password = cred->password_cb(cred);
195                 cred->password_obtained = CRED_SPECIFIED;
196         }
197
198         return cred->password;
199 }
200
201 /* Set a password on the credentials context, including an indication
202  * of 'how' the password was obtained */
203
204 BOOL cli_credentials_set_password(struct cli_credentials *cred, 
205                                   const char *val, 
206                                   enum credentials_obtained obtained)
207 {
208         if (obtained >= cred->password_obtained) {
209                 cred->password = talloc_strdup(cred, val);
210                 cred->password_obtained = obtained;
211
212                 cred->nt_hash = NULL;
213                 return True;
214         }
215
216         return False;
217 }
218
219 BOOL cli_credentials_set_password_callback(struct cli_credentials *cred,
220                                            const char *(*password_cb) (struct cli_credentials *))
221 {
222         if (cred->password_obtained < CRED_CALLBACK) {
223                 cred->password_cb = password_cb;
224                 cred->password_obtained = CRED_CALLBACK;
225                 return True;
226         }
227
228         return False;
229 }
230
231 /**
232  * Obtain the 'old' password for this credentials context (used for join accounts).
233  * @param cred credentials context
234  * @retval If set, the cleartext password, otherwise NULL
235  */
236 const char *cli_credentials_get_old_password(struct cli_credentials *cred)
237 {
238         if (cred->machine_account_pending) {
239                 cli_credentials_set_machine_account(cred);
240         }
241
242         return cred->old_password;
243 }
244
245 BOOL cli_credentials_set_old_password(struct cli_credentials *cred, 
246                                       const char *val, 
247                                       enum credentials_obtained obtained)
248 {
249         cred->old_password = talloc_strdup(cred, val);
250         return True;
251 }
252
253 /**
254  * Obtain the password, in the form MD4(unicode(password)) for this credentials context.
255  *
256  * Sometimes we only have this much of the password, while the rest of
257  * the time this call avoids calling E_md4hash themselves.
258  *
259  * @param cred credentials context
260  * @retval If set, the cleartext password, otherwise NULL
261  */
262 const struct samr_Password *cli_credentials_get_nt_hash(struct cli_credentials *cred, 
263                                                         TALLOC_CTX *mem_ctx)
264 {
265         const char *password = cli_credentials_get_password(cred);
266
267         if (password) {
268                 struct samr_Password *nt_hash = talloc(mem_ctx, struct samr_Password);
269                 if (!nt_hash) {
270                         return NULL;
271                 }
272                 
273                 E_md4hash(password, nt_hash->hash);    
274
275                 return nt_hash;
276         } else {
277                 return cred->nt_hash;
278         }
279 }
280
281 BOOL cli_credentials_set_nt_hash(struct cli_credentials *cred,
282                                  const struct samr_Password *nt_hash, 
283                                  enum credentials_obtained obtained)
284 {
285         if (obtained >= cred->password_obtained) {
286                 cli_credentials_set_password(cred, NULL, obtained);
287                 cred->nt_hash = talloc(cred, struct samr_Password);
288                 *cred->nt_hash = *nt_hash;
289                 return True;
290         }
291
292         return False;
293 }
294
295 /**
296  * Obtain the 'short' or 'NetBIOS' domain for this credentials context.
297  * @param cred credentials context
298  * @retval The domain set on this context. 
299  * @note Return value will never be NULL except by programmer error.
300  */
301 const char *cli_credentials_get_domain(struct cli_credentials *cred)
302 {
303         if (cred->machine_account_pending) {
304                 cli_credentials_set_machine_account(cred);
305         }
306
307         if (cred->domain_obtained == CRED_CALLBACK) {
308                 cred->domain = cred->domain_cb(cred);
309                 cred->domain_obtained = CRED_SPECIFIED;
310         }
311
312         return cred->domain;
313 }
314
315
316 BOOL cli_credentials_set_domain(struct cli_credentials *cred, 
317                                 const char *val, 
318                                 enum credentials_obtained obtained)
319 {
320         if (obtained >= cred->domain_obtained) {
321                 /* it is important that the domain be in upper case,
322                  * particularly for the sensitive NTLMv2
323                  * calculations */
324                 cred->domain = strupper_talloc(cred, val);
325                 cred->domain_obtained = obtained;
326                 return True;
327         }
328
329         return False;
330 }
331
332 BOOL cli_credentials_set_domain_callback(struct cli_credentials *cred,
333                                          const char *(*domain_cb) (struct cli_credentials *))
334 {
335         if (cred->domain_obtained < CRED_CALLBACK) {
336                 cred->domain_cb = domain_cb;
337                 cred->domain_obtained = CRED_CALLBACK;
338                 return True;
339         }
340
341         return False;
342 }
343
344 /**
345  * Obtain the Kerberos realm for this credentials context.
346  * @param cred credentials context
347  * @retval The realm set on this context. 
348  * @note Return value will never be NULL except by programmer error.
349  */
350 const char *cli_credentials_get_realm(struct cli_credentials *cred)
351 {       
352         if (cred->machine_account_pending) {
353                 cli_credentials_set_machine_account(cred);
354         }
355
356         if (cred->realm_obtained == CRED_CALLBACK) {
357                 cred->realm = cred->realm_cb(cred);
358                 cred->realm_obtained = CRED_SPECIFIED;
359         }
360
361         return cred->realm;
362 }
363
364 /**
365  * Set the realm for this credentials context, and force it to
366  * uppercase for the sainity of our local kerberos libraries 
367  */
368 BOOL cli_credentials_set_realm(struct cli_credentials *cred, 
369                                const char *val, 
370                                enum credentials_obtained obtained)
371 {
372         if (obtained >= cred->realm_obtained) {
373                 cred->realm = strupper_talloc(cred, val);
374                 cred->realm_obtained = obtained;
375                 return True;
376         }
377
378         return False;
379 }
380
381 BOOL cli_credentials_set_realm_callback(struct cli_credentials *cred,
382                                         const char *(*realm_cb) (struct cli_credentials *))
383 {
384         if (cred->realm_obtained < CRED_CALLBACK) {
385                 cred->realm_cb = realm_cb;
386                 cred->realm_obtained = CRED_CALLBACK;
387                 return True;
388         }
389
390         return False;
391 }
392
393 /**
394  * Obtain the 'short' or 'NetBIOS' workstation name for this credentials context.
395  *
396  * @param cred credentials context
397  * @retval The workstation name set on this context. 
398  * @note Return value will never be NULL except by programmer error.
399  */
400 const char *cli_credentials_get_workstation(struct cli_credentials *cred)
401 {
402         if (cred->workstation_obtained == CRED_CALLBACK) {
403                 cred->workstation = cred->workstation_cb(cred);
404                 cred->workstation_obtained = CRED_SPECIFIED;
405         }
406
407         return cred->workstation;
408 }
409
410 BOOL cli_credentials_set_workstation(struct cli_credentials *cred, 
411                                      const char *val, 
412                                      enum credentials_obtained obtained)
413 {
414         if (obtained >= cred->workstation_obtained) {
415                 cred->workstation = talloc_strdup(cred, val);
416                 cred->workstation_obtained = obtained;
417                 return True;
418         }
419
420         return False;
421 }
422
423 BOOL cli_credentials_set_workstation_callback(struct cli_credentials *cred,
424                                               const char *(*workstation_cb) (struct cli_credentials *))
425 {
426         if (cred->workstation_obtained < CRED_CALLBACK) {
427                 cred->workstation_cb = workstation_cb;
428                 cred->workstation_obtained = CRED_CALLBACK;
429                 return True;
430         }
431
432         return False;
433 }
434
435 /**
436  * Given a string, typically obtained from a -U argument, parse it into domain, username, realm and password fields
437  *
438  * The format accepted is [domain\\]user[%password] or user[@realm][%password]
439  *
440  * @param credentials Credentials structure on which to set the password
441  * @param data the string containing the username, password etc
442  * @param obtained This enum describes how 'specified' this password is
443  */
444
445 void cli_credentials_parse_string(struct cli_credentials *credentials, const char *data, enum credentials_obtained obtained)
446 {
447         char *uname, *p;
448
449         if (strcmp("%",data) == 0) {
450                 cli_credentials_set_anonymous(credentials);
451                 return;
452         }
453
454         uname = talloc_strdup(credentials, data); 
455         if ((p = strchr_m(uname,'%'))) {
456                 *p = 0;
457                 cli_credentials_set_password(credentials, p+1, obtained);
458         }
459
460         if ((p = strchr_m(uname,'@'))) {
461                 cli_credentials_set_principal(credentials, uname, obtained);
462                 *p = 0;
463                 cli_credentials_set_realm(credentials, p+1, obtained);
464                 return;
465         } else if ((p = strchr_m(uname,'\\')) || (p = strchr_m(uname, '/'))) {
466                 *p = 0;
467                 cli_credentials_set_domain(credentials, uname, obtained);
468                 uname = p+1;
469         }
470         cli_credentials_set_username(credentials, uname, obtained);
471 }
472
473 /**
474  * Specifies default values for domain, workstation and realm
475  * from the smb.conf configuration file
476  *
477  * @param cred Credentials structure to fill in
478  */
479 void cli_credentials_set_conf(struct cli_credentials *cred)
480 {
481         cli_credentials_set_username(cred, "", CRED_UNINITIALISED);
482         cli_credentials_set_domain(cred, lp_workgroup(), CRED_UNINITIALISED);
483         cli_credentials_set_workstation(cred, lp_netbios_name(), CRED_UNINITIALISED);
484         cli_credentials_set_realm(cred, lp_realm(), CRED_UNINITIALISED);
485 }
486
487 /**
488  * Guess defaults for credentials from environment variables, 
489  * and from the configuration file
490  * 
491  * @param cred Credentials structure to fill in
492  */
493 void cli_credentials_guess(struct cli_credentials *cred)
494 {
495         char *p;
496
497         cli_credentials_set_conf(cred);
498         
499         if (getenv("LOGNAME")) {
500                 cli_credentials_set_username(cred, getenv("LOGNAME"), CRED_GUESS_ENV);
501         }
502
503         if (getenv("USER")) {
504                 cli_credentials_parse_string(cred, getenv("USER"), CRED_GUESS_ENV);
505                 if ((p = strchr_m(getenv("USER"),'%'))) {
506                         memset(p,0,strlen(cred->password));
507                 }
508         }
509
510         if (getenv("DOMAIN")) {
511                 cli_credentials_set_domain(cred, getenv("DOMAIN"), CRED_GUESS_ENV);
512         }
513
514         if (getenv("PASSWD")) {
515                 cli_credentials_set_password(cred, getenv("PASSWD"), CRED_GUESS_ENV);
516         }
517
518         if (getenv("PASSWD_FD")) {
519                 cli_credentials_parse_password_fd(cred, atoi(getenv("PASSWD_FD")), CRED_GUESS_FILE);
520         }
521         
522         if (getenv("PASSWD_FILE")) {
523                 cli_credentials_parse_password_file(cred, getenv("PASSWD_FILE"), CRED_GUESS_FILE);
524         }
525
526         cli_credentials_set_ccache(cred, NULL, CRED_GUESS_FILE);
527 }
528
529 /**
530  * Attach NETLOGON credentials for use with SCHANNEL
531  */
532
533 void cli_credentials_set_netlogon_creds(struct cli_credentials *cred, 
534                                         struct creds_CredentialState *netlogon_creds)
535 {
536         cred->netlogon_creds = talloc_reference(cred, netlogon_creds);
537 }
538
539 /**
540  * Return attached NETLOGON credentials 
541  */
542
543 struct creds_CredentialState *cli_credentials_get_netlogon_creds(struct cli_credentials *cred)
544 {
545         return cred->netlogon_creds;
546 }
547
548 /** 
549  * Set NETLOGON secure channel type
550  */
551
552 void cli_credentials_set_secure_channel_type(struct cli_credentials *cred,
553                                              enum netr_SchannelType secure_channel_type)
554 {
555         cred->secure_channel_type = secure_channel_type;
556 }
557
558 /**
559  * Return NETLOGON secure chanel type
560  */
561
562 enum netr_SchannelType cli_credentials_get_secure_channel_type(struct cli_credentials *cred)
563 {
564         return cred->secure_channel_type;
565 }
566
567 /**
568  * Fill in a credentials structure as the anonymous user
569  */
570 void cli_credentials_set_anonymous(struct cli_credentials *cred) 
571 {
572         cli_credentials_set_username(cred, "", CRED_SPECIFIED);
573         cli_credentials_set_domain(cred, "", CRED_SPECIFIED);
574         cli_credentials_set_password(cred, NULL, CRED_SPECIFIED);
575 }
576
577 /**
578  * Describe a credentials context as anonymous or authenticated
579  * @retval True if anonymous, False if a username is specified
580  */
581
582 BOOL cli_credentials_is_anonymous(struct cli_credentials *cred)
583 {
584         const char *username;
585         
586         if (cred->machine_account_pending) {
587                 cli_credentials_set_machine_account(cred);
588         }
589
590         username = cli_credentials_get_username(cred);
591         
592         /* Yes, it is deliberate that we die if we have a NULL pointer
593          * here - anonymous is "", not NULL, which is 'never specified,
594          * never guessed', ie programmer bug */
595         if (!username[0]) {
596                 return True;
597         }
598
599         return False;
600 }