r9623: samba3dump now generates LDIF for the registry hives from registry.tdb
[sfrench/samba-autobuild/.git] / source4 / lib / credentials.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Copyright (C) Jelmer Vernooij 2005
5    Copyright (C) Tim Potter 2001
6    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "system/filesys.h"
25 #include "include/secrets.h"
26 #include "lib/ldb/include/ldb.h"
27 #include "librpc/gen_ndr/ndr_samr.h" /* for struct samrPassword */
28
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
106                 cred->nt_hash = NULL;
107                 return True;
108         }
109
110         return False;
111 }
112
113 /**
114  * Obtain the password for this credentials context.
115  * @param cred credentials context
116  * @retval If set, the cleartext password, otherwise NULL
117  */
118 const struct samr_Password *cli_credentials_get_nt_hash(struct cli_credentials *cred, 
119                                                         TALLOC_CTX *mem_ctx)
120 {
121         const char *password = cli_credentials_get_password(cred);
122
123         if (password) {
124                 struct samr_Password *nt_hash = talloc(mem_ctx, struct samr_Password);
125                 if (!nt_hash) {
126                         return NULL;
127                 }
128                 
129                 E_md4hash(password, nt_hash->hash);    
130
131                 return nt_hash;
132         } else {
133                 return cred->nt_hash;
134         }
135 }
136
137 BOOL cli_credentials_set_nt_hash(struct cli_credentials *cred,
138                                  const struct samr_Password *nt_hash, 
139                                  enum credentials_obtained obtained)
140 {
141         if (obtained >= cred->password_obtained) {
142                 cli_credentials_set_password(cred, NULL, obtained);
143                 cred->nt_hash = talloc(cred, struct samr_Password);
144                 *cred->nt_hash = *nt_hash;
145                 return True;
146         }
147
148         return False;
149 }
150
151 /**
152  * Obtain the 'short' or 'NetBIOS' domain for this credentials context.
153  * @param cred credentials context
154  * @retval The domain set on this context. 
155  * @note Return value will never be NULL except by programmer error.
156  */
157 const char *cli_credentials_get_domain(struct cli_credentials *cred)
158 {
159         if (cred->machine_account_pending) {
160                 cli_credentials_set_machine_account(cred);
161         }
162
163         if (cred->domain_obtained == CRED_CALLBACK) {
164                 cred->domain = cred->domain_cb(cred);
165                 cred->domain_obtained = CRED_SPECIFIED;
166         }
167
168         return cred->domain;
169 }
170
171
172 BOOL cli_credentials_set_domain(struct cli_credentials *cred, const char *val, enum credentials_obtained obtained)
173 {
174         if (obtained >= cred->domain_obtained) {
175                 cred->domain = talloc_strdup(cred, val);
176                 cred->domain_obtained = obtained;
177                 return True;
178         }
179
180         return False;
181 }
182
183 /**
184  * Obtain the Kerberos realm for this credentials context.
185  * @param cred credentials context
186  * @retval The realm set on this context. 
187  * @note Return value will never be NULL except by programmer error.
188  */
189 const char *cli_credentials_get_realm(struct cli_credentials *cred)
190 {       
191         if (cred->machine_account_pending) {
192                 cli_credentials_set_machine_account(cred);
193         }
194
195         if (cred->realm_obtained == CRED_CALLBACK) {
196                 cred->realm = cred->realm_cb(cred);
197                 cred->realm_obtained = CRED_SPECIFIED;
198         }
199
200         return cred->realm;
201 }
202
203 /**
204  * Obtain the user's Kerberos principal for this credentials context.
205  * @param cred credentials context
206  * @param mem_ctx A talloc context to return the prinipal name on.
207  * @retval The user's Kerberos principal
208  * @note Return value may be NULL due to out-of memeory or invalid mem_ctx
209  */
210 char *cli_credentials_get_principal(struct cli_credentials *cred,
211                                     TALLOC_CTX *mem_ctx)
212 {
213         return talloc_asprintf(mem_ctx, "%s@%s", 
214                                cli_credentials_get_username(cred),
215                                cli_credentials_get_realm(cred));
216 }
217
218 /**
219  * Set the realm for this credentials context, and force it to
220  * uppercase for the sainity of our local kerberos libraries 
221  */
222 BOOL cli_credentials_set_realm(struct cli_credentials *cred, const char *val, enum credentials_obtained obtained)
223 {
224         if (obtained >= cred->realm_obtained) {
225                 cred->realm = strupper_talloc(cred, val);
226                 cred->realm_obtained = obtained;
227                 return True;
228         }
229
230         return False;
231 }
232
233 /**
234  * Obtain the 'short' or 'NetBIOS' workstation name for this credentials context.
235  *
236  * @param cred credentials context
237  * @retval The workstation name set on this context. 
238  * @note Return value will never be NULL except by programmer error.
239  */
240 const char *cli_credentials_get_workstation(struct cli_credentials *cred)
241 {
242         if (cred->workstation_obtained == CRED_CALLBACK) {
243                 cred->workstation = cred->workstation_cb(cred);
244                 cred->workstation_obtained = CRED_SPECIFIED;
245         }
246
247         return cred->workstation;
248 }
249
250 BOOL cli_credentials_set_workstation(struct cli_credentials *cred, const char *val, enum credentials_obtained obtained)
251 {
252         if (obtained >= cred->workstation_obtained) {
253                 cred->workstation = talloc_strdup(cred, val);
254                 cred->workstation_obtained = obtained;
255                 return True;
256         }
257
258         return False;
259 }
260
261 /**
262  * Read a file descriptor, and parse it for a password (eg from a file or stdin)
263  *
264  * @param credentials Credentials structure on which to set the password
265  * @param fd open file descriptor to read the password from 
266  * @param obtained This enum describes how 'specified' this password is
267  */
268
269 BOOL cli_credentials_parse_password_fd(struct cli_credentials *credentials, int fd, enum credentials_obtained obtained)
270 {
271         char *p;
272         char pass[128];
273
274         for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
275                 p && p - pass < sizeof(pass);) {
276                 switch (read(fd, p, 1)) {
277                 case 1:
278                         if (*p != '\n' && *p != '\0') {
279                                 *++p = '\0'; /* advance p, and null-terminate pass */
280                                 break;
281                         }
282                 case 0:
283                         if (p - pass) {
284                                 *p = '\0'; /* null-terminate it, just in case... */
285                                 p = NULL; /* then force the loop condition to become false */
286                                 break;
287                         } else {
288                                 fprintf(stderr, "Error reading password from file descriptor %d: %s\n", fd, "empty password\n");
289                                 return False;
290                         }
291
292                 default:
293                         fprintf(stderr, "Error reading password from file descriptor %d: %s\n",
294                                         fd, strerror(errno));
295                         return False;
296                 }
297         }
298
299         cli_credentials_set_password(credentials, pass, obtained);
300         return True;
301 }
302
303 /**
304  * Read a named file, and parse it for a password
305  *
306  * @param credentials Credentials structure on which to set the password
307  * @param file a named file to read the password from 
308  * @param obtained This enum describes how 'specified' this password is
309  */
310
311 BOOL cli_credentials_parse_password_file(struct cli_credentials *credentials, const char *file, enum credentials_obtained obtained)
312 {
313         int fd = open(file, O_RDONLY, 0);
314         BOOL ret;
315
316         if (fd < 0) {
317                 fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
318                                 file, strerror(errno));
319                 return False;
320         }
321
322         ret = cli_credentials_parse_password_fd(credentials, fd, obtained);
323
324         close(fd);
325         
326         return ret;
327 }
328
329 /**
330  * Read a named file, and parse it for username, domain, realm and password
331  *
332  * @param credentials Credentials structure on which to set the password
333  * @param file a named file to read the details from 
334  * @param obtained This enum describes how 'specified' this password is
335  */
336
337 BOOL cli_credentials_parse_file(struct cli_credentials *cred, const char *file, enum credentials_obtained obtained) 
338 {
339         XFILE *auth;
340         char buf[128];
341         uint16_t len = 0;
342         char *ptr, *val, *param;
343
344         if ((auth=x_fopen(file, O_RDONLY, 0)) == NULL)
345         {
346                 /* fail if we can't open the credentials file */
347                 d_printf("ERROR: Unable to open credentials file!\n");
348                 return False;
349         }
350
351         while (!x_feof(auth))
352         {
353                 /* get a line from the file */
354                 if (!x_fgets(buf, sizeof(buf), auth))
355                         continue;
356                 len = strlen(buf);
357
358                 if ((len) && (buf[len-1]=='\n'))
359                 {
360                         buf[len-1] = '\0';
361                         len--;
362                 }
363                 if (len == 0)
364                         continue;
365
366                 /* break up the line into parameter & value.
367                  * will need to eat a little whitespace possibly */
368                 param = buf;
369                 if (!(ptr = strchr_m (buf, '=')))
370                         continue;
371
372                 val = ptr+1;
373                 *ptr = '\0';
374
375                 /* eat leading white space */
376                 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
377                         val++;
378
379                 if (strwicmp("password", param) == 0) {
380                         cli_credentials_set_password(cred, val, obtained);
381                 } else if (strwicmp("username", param) == 0) {
382                         cli_credentials_set_username(cred, val, obtained);
383                 } else if (strwicmp("domain", param) == 0) {
384                         cli_credentials_set_domain(cred, val, obtained);
385                 } else if (strwicmp("realm", param) == 0) {
386                         cli_credentials_set_realm(cred, val, obtained);
387                 }
388                 memset(buf, 0, sizeof(buf));
389         }
390
391         x_fclose(auth);
392         return True;
393 }
394
395
396 /**
397  * Given a string, typically obtained from a -U argument, parse it into domain, username, realm and password fields
398  *
399  * The format accepted is [domain\\]user[%password] or user[@realm][%password]
400  *
401  * @param credentials Credentials structure on which to set the password
402  * @param data the string containing the username, password etc
403  * @param obtained This enum describes how 'specified' this password is
404  */
405
406 void cli_credentials_parse_string(struct cli_credentials *credentials, const char *data, enum credentials_obtained obtained)
407 {
408         char *uname, *p;
409
410         if (strcmp("%",data) == 0) {
411                 cli_credentials_set_anonymous(credentials);
412                 return;
413         }
414
415         uname = talloc_strdup(credentials, data); 
416         if ((p = strchr_m(uname,'%'))) {
417                 *p = 0;
418                 cli_credentials_set_password(credentials, p+1, obtained);
419         }
420
421         if ((p = strchr_m(uname,'@'))) {
422                 *p = 0;
423                 cli_credentials_set_realm(credentials, p+1, obtained);
424         } else if ((p = strchr_m(uname,'\\')) || (p = strchr_m(uname, '/'))) {
425                 *p = 0;
426                 cli_credentials_set_domain(credentials, uname, obtained);
427                 uname = p+1;
428         }
429         cli_credentials_set_username(credentials, uname, obtained);
430 }
431
432 /**
433  * Specifies default values for domain, workstation and realm
434  * from the smb.conf configuration file
435  *
436  * @param cred Credentials structure to fill in
437  */
438 void cli_credentials_set_conf(struct cli_credentials *cred)
439 {
440         cli_credentials_set_domain(cred, lp_workgroup(), CRED_GUESSED);
441         cli_credentials_set_workstation(cred, lp_netbios_name(), CRED_GUESSED);
442         cli_credentials_set_realm(cred, lp_realm(), CRED_GUESSED);
443 }
444
445 /**
446  * Guess defaults for credentials from environment variables, 
447  * and from the configuration file
448  * 
449  * @param cred Credentials structure to fill in
450  */
451 void cli_credentials_guess(struct cli_credentials *cred)
452 {
453         char *p;
454
455         cli_credentials_set_username(cred, "", CRED_GUESSED);
456         cli_credentials_set_conf(cred);
457         
458         if (getenv("LOGNAME")) {
459                 cli_credentials_set_username(cred, getenv("LOGNAME"), CRED_GUESSED);
460         }
461
462         if (getenv("USER")) {
463                 cli_credentials_parse_string(cred, getenv("USER"), CRED_GUESSED);
464                 if ((p = strchr_m(getenv("USER"),'%'))) {
465                         memset(p,0,strlen(cred->password));
466                 }
467         }
468
469         if (getenv("DOMAIN")) {
470                 cli_credentials_set_domain(cred, getenv("DOMAIN"), CRED_GUESSED);
471         }
472
473         if (getenv("PASSWD")) {
474                 cli_credentials_set_password(cred, getenv("PASSWD"), CRED_GUESSED);
475         }
476
477         if (getenv("PASSWD_FD")) {
478                 cli_credentials_parse_password_fd(cred, atoi(getenv("PASSWD_FD")), CRED_GUESSED);
479         }
480         
481         if (getenv("PASSWD_FILE")) {
482                 cli_credentials_parse_password_file(cred, getenv("PASSWD_FILE"), CRED_GUESSED);
483         }
484 }
485
486 /**
487  * Fill in credentials for the machine trust account, from the secrets database.
488  * 
489  * @param cred Credentials structure to fill in
490  * @retval NTSTATUS error detailing any failure
491  */
492 NTSTATUS cli_credentials_set_machine_account(struct cli_credentials *cred)
493 {
494         TALLOC_CTX *mem_ctx;
495         
496         struct ldb_context *ldb;
497         int ldb_ret;
498         struct ldb_message **msgs;
499         const char *attrs[] = {
500                 "secret",
501                 "samAccountName",
502                 "flatname",
503                 "realm",
504                 "secureChannelType",
505                 "ntPwdHash",
506                 "msDS-KeyVersionNumber",
507                 NULL
508         };
509         
510         const char *machine_account;
511         const char *password;
512         const char *domain;
513         const char *realm;
514         enum netr_SchannelType sct;
515         
516         /* ok, we are going to get it now, don't recurse back here */
517         cred->machine_account_pending = False;
518
519         mem_ctx = talloc_named(cred, 0, "cli_credentials fetch machine password");
520         /* Local secrets are stored in secrets.ldb */
521         ldb = secrets_db_connect(mem_ctx);
522         if (!ldb) {
523                 DEBUG(1, ("Could not open secrets.ldb\n"));
524                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
525         }
526
527         /* search for the secret record */
528         ldb_ret = gendb_search(ldb,
529                                mem_ctx, ldb_dn_explode(mem_ctx, SECRETS_PRIMARY_DOMAIN_DN), 
530                                &msgs, attrs,
531                                SECRETS_PRIMARY_DOMAIN_FILTER, 
532                                cli_credentials_get_domain(cred));
533         if (ldb_ret == 0) {
534                 DEBUG(1, ("Could not find join record to domain: %s\n",
535                           cli_credentials_get_domain(cred)));
536                 talloc_free(mem_ctx);
537                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
538         } else if (ldb_ret != 1) {
539                 DEBUG(1, ("Found more than one (%d) join records to domain: %s\n",
540                           ldb_ret, cli_credentials_get_domain(cred)));
541                 talloc_free(mem_ctx);
542                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
543         }
544         
545         password = ldb_msg_find_string(msgs[0], "secret", NULL);
546
547         machine_account = ldb_msg_find_string(msgs[0], "samAccountName", NULL);
548
549         if (!machine_account) {
550                 DEBUG(1, ("Could not find 'samAccountName' in join record to domain: %s\n",
551                           cli_credentials_get_domain(cred)));
552                 talloc_free(mem_ctx);
553                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
554         }
555         
556         sct = ldb_msg_find_int(msgs[0], "secureChannelType", 0);
557         if (!sct) { 
558                 DEBUG(1, ("Domain join for acocunt %s did not have a secureChannelType set!\n",
559                           machine_account));
560                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
561         }
562         
563         if (!password) {
564                 const struct ldb_val *nt_password_hash = ldb_msg_find_ldb_val(msgs[0], "ntPwdHash");
565                 struct samr_Password hash;
566                 ZERO_STRUCT(hash);
567                 if (nt_password_hash) {
568                         memcpy(hash.hash, nt_password_hash->data, 
569                                MIN(nt_password_hash->length, sizeof(hash.hash)));
570                 
571                         cli_credentials_set_nt_hash(cred, &hash, CRED_SPECIFIED);
572                 } else {
573                 
574                         DEBUG(1, ("Could not find 'secret' in join record to domain: %s\n",
575                                   cli_credentials_get_domain(cred)));
576                         talloc_free(mem_ctx);
577                         return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
578                 }
579         }
580         
581         cli_credentials_set_secure_channel_type(cred, sct);
582
583         domain = ldb_msg_find_string(msgs[0], "flatname", NULL);
584         if (domain) {
585                 cli_credentials_set_domain(cred, domain, CRED_SPECIFIED);
586         }
587
588         realm = ldb_msg_find_string(msgs[0], "realm", NULL);
589         if (realm) {
590                 cli_credentials_set_realm(cred, realm, CRED_SPECIFIED);
591         }
592
593         cli_credentials_set_username(cred, machine_account, CRED_SPECIFIED);
594         if (password) {
595                 cli_credentials_set_password(cred, password, CRED_SPECIFIED);
596         }
597
598         cli_credentials_set_kvno(cred, ldb_msg_find_int(msgs[0], "msDS-KeyVersionNumber", 0));
599         
600         talloc_free(mem_ctx);
601         
602         return NT_STATUS_OK;
603 }
604
605 /**
606  * Ask that when required, the credentials system will be filled with
607  * machine trust account, from the secrets database.
608  * 
609  * @param cred Credentials structure to fill in
610  * @note This function is used to call the above function after, rather 
611  *       than during, popt processing.
612  *
613  */
614 void cli_credentials_set_machine_account_pending(struct cli_credentials *cred)
615 {
616         cred->machine_account_pending = True;
617 }
618
619 /**
620  * Attach NETLOGON credentials for use with SCHANNEL
621  */
622
623 void cli_credentials_set_netlogon_creds(struct cli_credentials *cred, 
624                                         struct creds_CredentialState *netlogon_creds)
625 {
626         cred->netlogon_creds = talloc_reference(cred, netlogon_creds);
627 }
628
629 /**
630  * Return attached NETLOGON credentials 
631  */
632
633 struct creds_CredentialState *cli_credentials_get_netlogon_creds(struct cli_credentials *cred)
634 {
635         return cred->netlogon_creds;
636 }
637
638 /** 
639  * Set NETLOGON secure channel type
640  */
641
642 void cli_credentials_set_secure_channel_type(struct cli_credentials *cred,
643                                              enum netr_SchannelType secure_channel_type)
644 {
645         cred->secure_channel_type = secure_channel_type;
646 }
647
648 /**
649  * Return NETLOGON secure chanel type
650  */
651
652 enum netr_SchannelType cli_credentials_get_secure_channel_type(struct cli_credentials *cred)
653 {
654         return cred->secure_channel_type;
655 }
656
657 /** 
658  * Set Kerberos KVNO
659  */
660
661 void cli_credentials_set_kvno(struct cli_credentials *cred,
662                               int kvno)
663 {
664         cred->kvno = kvno;
665 }
666
667 /**
668  * Return Kerberos KVNO
669  */
670
671 int cli_credentials_get_kvno(struct cli_credentials *cred)
672 {
673         return cred->kvno;
674 }
675
676 /**
677  * Fill in a credentials structure as the anonymous user
678  */
679 void cli_credentials_set_anonymous(struct cli_credentials *cred) 
680 {
681         cli_credentials_set_username(cred, "", CRED_SPECIFIED);
682         cli_credentials_set_domain(cred, "", CRED_SPECIFIED);
683         cli_credentials_set_password(cred, NULL, CRED_SPECIFIED);
684 }
685
686 /**
687  * Describe a credentials context as anonymous or authenticated
688  * @retval True if anonymous, False if a username is specified
689  */
690
691 BOOL cli_credentials_is_anonymous(struct cli_credentials *cred)
692 {
693         const char *username = cli_credentials_get_username(cred);
694
695         /* Yes, it is deliberate that we die if we have a NULL pointer
696          * here - anonymous is "", not NULL, which is 'never specified,
697          * never guessed', ie programmer bug */
698         if (!username[0]) 
699                 return True;
700
701         return False;
702 }