db796cde3543549100c3cb88fbcff4c4fe39006c
[samba.git] / source / 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 "lib/cmdline/popt_common.h"
26 #include "include/secrets.h"
27 #include "lib/ldb/include/ldb.h"
28
29 /**
30  * Create a new credentials structure
31  * @param mem_ctx TALLOC_CTX parent for credentials structure 
32  */
33 struct cli_credentials *cli_credentials_init(TALLOC_CTX *mem_ctx) 
34 {
35         struct cli_credentials *cred = talloc(mem_ctx, struct cli_credentials);
36         if (!cred) {
37                 return cred;
38         }
39
40         cred->netlogon_creds = NULL;
41         cred->machine_account_pending = False;
42         cred->workstation_obtained = CRED_UNINITIALISED;
43         cred->username_obtained = CRED_UNINITIALISED;
44         cred->password_obtained = CRED_UNINITIALISED;
45         cred->domain_obtained = CRED_UNINITIALISED;
46         cred->realm_obtained = CRED_UNINITIALISED;
47         return cred;
48 }
49
50 /**
51  * Obtain the username for this credentials context.
52  * @param cred credentials context
53  * @retval The username set on this context.
54  * @note Return value will never be NULL except by programmer error.
55  */
56 const char *cli_credentials_get_username(struct cli_credentials *cred)
57 {
58         if (cred->machine_account_pending) {
59                 cli_credentials_set_machine_account(cred);
60         }
61
62         if (cred->username_obtained == CRED_CALLBACK) {
63                 cred->username = cred->username_cb(cred);
64                 cred->username_obtained = CRED_SPECIFIED;
65         }
66
67         return cred->username;
68 }
69
70 BOOL cli_credentials_set_username(struct cli_credentials *cred, const char *val, enum credentials_obtained obtained)
71 {
72         if (obtained >= cred->username_obtained) {
73                 cred->username = talloc_strdup(cred, val);
74                 cred->username_obtained = obtained;
75                 return True;
76         }
77
78         return False;
79 }
80
81 /**
82  * Obtain the password for this credentials context.
83  * @param cred credentials context
84  * @retval If set, the cleartext password, otherwise NULL
85  */
86 const char *cli_credentials_get_password(struct cli_credentials *cred)
87 {
88         if (cred->machine_account_pending) {
89                 cli_credentials_set_machine_account(cred);
90         }
91
92         if (cred->password_obtained == CRED_CALLBACK) {
93                 cred->password = cred->password_cb(cred);
94                 cred->password_obtained = CRED_SPECIFIED;
95         }
96
97         return cred->password;
98 }
99
100 BOOL cli_credentials_set_password(struct cli_credentials *cred, const char *val, enum credentials_obtained obtained)
101 {
102         if (obtained >= cred->password_obtained) {
103                 cred->password = talloc_strdup(cred, val);
104                 cred->password_obtained = obtained;
105                 return True;
106         }
107
108         return False;
109 }
110
111 /**
112  * Obtain the 'short' or 'NetBIOS' domain for this credentials context.
113  * @param cred credentials context
114  * @retval The domain set on this context. 
115  * @note Return value will never be NULL except by programmer error.
116  */
117 const char *cli_credentials_get_domain(struct cli_credentials *cred)
118 {
119         if (cred->machine_account_pending) {
120                 cli_credentials_set_machine_account(cred);
121         }
122
123         if (cred->domain_obtained == CRED_CALLBACK) {
124                 cred->domain = cred->domain_cb(cred);
125                 cred->domain_obtained = CRED_SPECIFIED;
126         }
127
128         return cred->domain;
129 }
130
131
132 BOOL cli_credentials_set_domain(struct cli_credentials *cred, const char *val, enum credentials_obtained obtained)
133 {
134         if (obtained >= cred->domain_obtained) {
135                 cred->domain = talloc_strdup(cred, val);
136                 cred->domain_obtained = obtained;
137                 return True;
138         }
139
140         return False;
141 }
142
143 /**
144  * Obtain the Kerberos realm for this credentials context.
145  * @param cred credentials context
146  * @retval The realm set on this context. 
147  * @note Return value will never be NULL except by programmer error.
148  */
149 const char *cli_credentials_get_realm(struct cli_credentials *cred)
150 {       
151         if (cred->machine_account_pending) {
152                 cli_credentials_set_machine_account(cred);
153         }
154
155         if (cred->realm_obtained == CRED_CALLBACK) {
156                 cred->realm = cred->realm_cb(cred);
157                 cred->realm_obtained = CRED_SPECIFIED;
158         }
159
160         return cred->realm;
161 }
162
163 /**
164  * Obtain the user's Kerberos principal for this credentials context.
165  * @param cred credentials context
166  * @param mem_ctx A talloc context to return the prinipal name on.
167  * @retval The user's Kerberos principal
168  * @note Return value may be NULL due to out-of memeory or invalid mem_ctx
169  */
170 char *cli_credentials_get_principal(struct cli_credentials *cred,
171                                     TALLOC_CTX *mem_ctx)
172 {
173         return talloc_asprintf(mem_ctx, "%s@%s", 
174                                cli_credentials_get_username(cred),
175                                cli_credentials_get_realm(cred));
176 }
177
178 BOOL cli_credentials_set_realm(struct cli_credentials *cred, const char *val, enum credentials_obtained obtained)
179 {
180         if (obtained >= cred->realm_obtained) {
181                 cred->realm = talloc_strdup(cred, val);
182                 cred->realm_obtained = obtained;
183                 return True;
184         }
185
186         return False;
187 }
188
189 /**
190  * Obtain the 'short' or 'NetBIOS' workstation name for this credentials context.
191  *
192  * @param cred credentials context
193  * @retval The workstation name set on this context. 
194  * @note Return value will never be NULL except by programmer error.
195  */
196 const char *cli_credentials_get_workstation(struct cli_credentials *cred)
197 {
198         if (cred->workstation_obtained == CRED_CALLBACK) {
199                 cred->workstation = cred->workstation_cb(cred);
200                 cred->workstation_obtained = CRED_SPECIFIED;
201         }
202
203         return cred->workstation;
204 }
205
206 BOOL cli_credentials_set_workstation(struct cli_credentials *cred, const char *val, enum credentials_obtained obtained)
207 {
208         if (obtained >= cred->workstation_obtained) {
209                 cred->workstation = talloc_strdup(cred, val);
210                 cred->workstation_obtained = obtained;
211                 return True;
212         }
213
214         return False;
215 }
216
217 /**
218  * Read a file descriptor, and parse it for a password (eg from a file or stdin)
219  *
220  * @param credentials Credentials structure on which to set the password
221  * @param fd open file descriptor to read the password from 
222  * @param obtained This enum describes how 'specified' this password is
223  */
224
225 BOOL cli_credentials_parse_password_fd(struct cli_credentials *credentials, int fd, enum credentials_obtained obtained)
226 {
227         char *p;
228         char pass[128];
229
230         for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
231                 p && p - pass < sizeof(pass);) {
232                 switch (read(fd, p, 1)) {
233                 case 1:
234                         if (*p != '\n' && *p != '\0') {
235                                 *++p = '\0'; /* advance p, and null-terminate pass */
236                                 break;
237                         }
238                 case 0:
239                         if (p - pass) {
240                                 *p = '\0'; /* null-terminate it, just in case... */
241                                 p = NULL; /* then force the loop condition to become false */
242                                 break;
243                         } else {
244                                 fprintf(stderr, "Error reading password from file descriptor %d: %s\n", fd, "empty password\n");
245                                 return False;
246                         }
247
248                 default:
249                         fprintf(stderr, "Error reading password from file descriptor %d: %s\n",
250                                         fd, strerror(errno));
251                         return False;
252                 }
253         }
254
255         cli_credentials_set_password(credentials, pass, obtained);
256         return True;
257 }
258
259 /**
260  * Read a named file, and parse it for a password
261  *
262  * @param credentials Credentials structure on which to set the password
263  * @param file a named file to read the password from 
264  * @param obtained This enum describes how 'specified' this password is
265  */
266
267 BOOL cli_credentials_parse_password_file(struct cli_credentials *credentials, const char *file, enum credentials_obtained obtained)
268 {
269         int fd = open(file, O_RDONLY, 0);
270         BOOL ret;
271
272         if (fd < 0) {
273                 fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
274                                 file, strerror(errno));
275                 return False;
276         }
277
278         ret = cli_credentials_parse_password_fd(credentials, fd, obtained);
279
280         close(fd);
281         
282         return ret;
283 }
284
285 /**
286  * Read a named file, and parse it for username, domain, realm and password
287  *
288  * @param credentials Credentials structure on which to set the password
289  * @param file a named file to read the details from 
290  * @param obtained This enum describes how 'specified' this password is
291  */
292
293 BOOL cli_credentials_parse_file(struct cli_credentials *cred, const char *file, enum credentials_obtained obtained) 
294 {
295         XFILE *auth;
296         char buf[128];
297         uint16_t len = 0;
298         char *ptr, *val, *param;
299
300         if ((auth=x_fopen(file, O_RDONLY, 0)) == NULL)
301         {
302                 /* fail if we can't open the credentials file */
303                 d_printf("ERROR: Unable to open credentials file!\n");
304                 return False;
305         }
306
307         while (!x_feof(auth))
308         {
309                 /* get a line from the file */
310                 if (!x_fgets(buf, sizeof(buf), auth))
311                         continue;
312                 len = strlen(buf);
313
314                 if ((len) && (buf[len-1]=='\n'))
315                 {
316                         buf[len-1] = '\0';
317                         len--;
318                 }
319                 if (len == 0)
320                         continue;
321
322                 /* break up the line into parameter & value.
323                  * will need to eat a little whitespace possibly */
324                 param = buf;
325                 if (!(ptr = strchr_m (buf, '=')))
326                         continue;
327
328                 val = ptr+1;
329                 *ptr = '\0';
330
331                 /* eat leading white space */
332                 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
333                         val++;
334
335                 if (strwicmp("password", param) == 0) {
336                         cli_credentials_set_password(cred, val, obtained);
337                 } else if (strwicmp("username", param) == 0) {
338                         cli_credentials_set_username(cred, val, obtained);
339                 } else if (strwicmp("domain", param) == 0) {
340                         cli_credentials_set_domain(cred, val, obtained);
341                 } else if (strwicmp("realm", param) == 0) {
342                         cli_credentials_set_realm(cred, val, obtained);
343                 }
344                 memset(buf, 0, sizeof(buf));
345         }
346
347         x_fclose(auth);
348         return True;
349 }
350
351
352 /**
353  * Given a string, typically obtained from a -U argument, parse it into domain, username, realm and password fields
354  *
355  * The format accepted is [domain\\]user[%password] or user[@realm][%password]
356  *
357  * @param credentials Credentials structure on which to set the password
358  * @param data the string containing the username, password etc
359  * @param obtained This enum describes how 'specified' this password is
360  */
361
362 void cli_credentials_parse_string(struct cli_credentials *credentials, const char *data, enum credentials_obtained obtained)
363 {
364         char *uname, *p;
365
366         if (strcmp("%",data) == 0) {
367                 cli_credentials_set_anonymous(credentials);
368                 return;
369         }
370
371         uname = talloc_strdup(credentials, data); 
372         if ((p = strchr_m(uname,'%'))) {
373                 *p = 0;
374                 cli_credentials_set_password(credentials, p+1, obtained);
375         }
376
377         if ((p = strchr_m(uname,'@'))) {
378                 *p = 0;
379                 cli_credentials_set_realm(credentials, p+1, obtained);
380         } else if ((p = strchr_m(uname,'\\')) || (p = strchr_m(uname, '/'))) {
381                 *p = 0;
382                 cli_credentials_set_domain(credentials, uname, obtained);
383                 uname = p+1;
384         }
385         cli_credentials_set_username(credentials, uname, obtained);
386 }
387
388 /**
389  * Specifies default values for domain, workstation and realm
390  * from the smb.conf configuration file
391  *
392  * @param cred Credentials structure to fill in
393  */
394 void cli_credentials_set_conf(struct cli_credentials *cred)
395 {
396         cli_credentials_set_domain(cred, lp_workgroup(), CRED_GUESSED);
397         cli_credentials_set_workstation(cred, lp_netbios_name(), CRED_GUESSED);
398         cli_credentials_set_realm(cred, lp_realm(), CRED_GUESSED);
399 }
400
401 /**
402  * Guess defaults for credentials from environment variables, 
403  * and from the configuration file
404  * 
405  * @param cred Credentials structure to fill in
406  */
407 void cli_credentials_guess(struct cli_credentials *cred)
408 {
409         char *p;
410
411         cli_credentials_set_username(cred, "", CRED_GUESSED);
412         cli_credentials_set_conf(cred);
413         
414         if (getenv("LOGNAME")) {
415                 cli_credentials_set_username(cred, getenv("LOGNAME"), CRED_GUESSED);
416         }
417
418         if (getenv("USER")) {
419                 cli_credentials_parse_string(cred, getenv("USER"), CRED_GUESSED);
420                 if ((p = strchr_m(getenv("USER"),'%'))) {
421                         memset(p,0,strlen(cred->password));
422                 }
423         }
424
425         if (getenv("DOMAIN")) {
426                 cli_credentials_set_domain(cred, getenv("DOMAIN"), CRED_GUESSED);
427         }
428
429         if (getenv("PASSWD")) {
430                 cli_credentials_set_password(cred, getenv("PASSWD"), CRED_GUESSED);
431         }
432
433         if (getenv("PASSWD_FD")) {
434                 cli_credentials_parse_password_fd(cred, atoi(getenv("PASSWD_FD")), CRED_GUESSED);
435         }
436         
437         if (getenv("PASSWD_FILE")) {
438                 cli_credentials_parse_password_file(cred, getenv("PASSWD_FILE"), CRED_GUESSED);
439         }
440 }
441
442 /**
443  * Fill in credentials for the machine trust account, from the secrets database.
444  * 
445  * @param cred Credentials structure to fill in
446  * @retval NTSTATUS error detailing any failure
447  */
448 NTSTATUS cli_credentials_set_machine_account(struct cli_credentials *cred)
449 {
450         TALLOC_CTX *mem_ctx;
451         
452         struct ldb_context *ldb;
453         int ldb_ret;
454         struct ldb_message **msgs;
455         const char *base_dn = SECRETS_PRIMARY_DOMAIN_DN;
456         const char *attrs[] = {
457                 "secret",
458                 "samAccountName",
459                 "flatname",
460                 "realm",
461                 NULL
462         };
463         
464         const char *machine_account;
465         const char *password;
466         const char *domain;
467         const char *realm;
468         
469         /* ok, we are going to get it now, don't recurse back here */
470         cred->machine_account_pending = False;
471
472         mem_ctx = talloc_named(cred, 0, "cli_credentials fetch machine password");
473         /* Local secrets are stored in secrets.ldb */
474         ldb = secrets_db_connect(mem_ctx);
475         if (!ldb) {
476                 DEBUG(1, ("Could not open secrets.ldb\n"));
477                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
478         }
479
480         /* search for the secret record */
481         ldb_ret = gendb_search(ldb,
482                                mem_ctx, base_dn, &msgs, attrs,
483                                SECRETS_PRIMARY_DOMAIN_FILTER, 
484                                cli_credentials_get_domain(cred));
485         if (ldb_ret == 0) {
486                 DEBUG(1, ("Could not find join record to domain: %s\n",
487                           cli_credentials_get_domain(cred)));
488                 talloc_free(mem_ctx);
489                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
490         } else if (ldb_ret != 1) {
491                 DEBUG(1, ("Found more than one (%d) join records to domain: %s\n",
492                           ldb_ret, cli_credentials_get_domain(cred)));
493                 talloc_free(mem_ctx);
494                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
495         }
496         
497         password = ldb_msg_find_string(msgs[0], "secret", NULL);
498         if (!password) {
499                 DEBUG(1, ("Could not find 'secret' in join record to domain: %s\n",
500                           cli_credentials_get_domain(cred)));
501                 talloc_free(mem_ctx);
502                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
503         }
504         
505         machine_account = ldb_msg_find_string(msgs[0], "samAccountName", NULL);
506         if (!machine_account) {
507                 DEBUG(1, ("Could not find 'samAccountName' in join record to domain: %s\n",
508                           cli_credentials_get_domain(cred)));
509                 talloc_free(mem_ctx);
510                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
511         }
512         
513         domain = ldb_msg_find_string(msgs[0], "flatname", NULL);
514         if (domain) {
515                 cli_credentials_set_domain(cred, domain, CRED_SPECIFIED);
516         }
517
518         realm = ldb_msg_find_string(msgs[0], "realm", NULL);
519         if (realm) {
520                 cli_credentials_set_realm(cred, realm, CRED_SPECIFIED);
521         }
522         
523         cli_credentials_set_username(cred, machine_account, CRED_SPECIFIED);
524         cli_credentials_set_password(cred, password, CRED_SPECIFIED);
525         talloc_free(mem_ctx);
526         
527         return NT_STATUS_OK;
528 }
529
530 /**
531  * Ask that when required, the credentials system will be filled with
532  * machine trust account, from the secrets database.
533  * 
534  * @param cred Credentials structure to fill in
535  * @note This function is used to call the above function after, rather 
536  *       than during, popt processing.
537  *
538  */
539 void cli_credentials_set_machine_account_pending(struct cli_credentials *cred)
540 {
541         cred->machine_account_pending = True;
542 }
543
544 /**
545  * Attach NETLOGON credentials for use with SCHANNEL
546  */
547
548 void cli_credentials_set_netlogon_creds(struct cli_credentials *cred, 
549                                         struct creds_CredentialState *netlogon_creds)
550 {
551         cred->netlogon_creds = talloc_reference(cred, netlogon_creds);
552 }
553
554 /**
555  * Return attached NETLOGON credentials 
556  */
557
558 struct creds_CredentialState *cli_credentials_get_netlogon_creds(struct cli_credentials *cred)
559 {
560         return cred->netlogon_creds;
561 }
562
563 /**
564  * Fill in a credentials structure as the anonymous user
565  */
566 void cli_credentials_set_anonymous(struct cli_credentials *cred) 
567 {
568         cli_credentials_set_username(cred, "", CRED_SPECIFIED);
569         cli_credentials_set_domain(cred, "", CRED_SPECIFIED);
570         cli_credentials_set_password(cred, NULL, CRED_SPECIFIED);
571 }
572
573 /**
574  * Describe a credentials context as anonymous or authenticated
575  * @retval True if anonymous, False if a username is specified
576  */
577
578 BOOL cli_credentials_is_anonymous(struct cli_credentials *cred)
579 {
580         const char *username = cli_credentials_get_username(cred);
581
582         /* Yes, it is deliberate that we die if we have a NULL pointer
583          * here - anonymous is "", not NULL, which is 'never specified,
584          * never guessed', ie programmer bug */
585         if (!username[0]) 
586                 return True;
587
588         return False;
589 }