r7827: Add in-memory keytab to Samba4, using the new MEMORY_WILDCARD keytab
[samba.git] / source / auth / kerberos / kerberos_util.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Kerberos utility functions for GENSEC
5    
6    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
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    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "system/kerberos.h"
26 #include "system/time.h"
27 #include "system/network.h"
28 #include "auth/kerberos/kerberos.h"
29 #include "auth/auth.h"
30
31 struct principal_container {
32         struct smb_krb5_context *smb_krb5_context;
33         krb5_principal principal;
34 };
35
36 struct ccache_container {
37         struct smb_krb5_context *smb_krb5_context;
38         krb5_ccache ccache;
39 };
40
41 struct keytab_container {
42         struct smb_krb5_context *smb_krb5_context;
43         krb5_keytab keytab;
44 };
45
46 static int free_principal(void *ptr) {
47         struct principal_container *pc = ptr;
48         /* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */
49         krb5_free_principal(pc->smb_krb5_context->krb5_context, pc->principal);
50
51         return 0;
52 }
53
54 krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx, 
55                                                 struct cli_credentials *machine_account, 
56                                                 struct smb_krb5_context *smb_krb5_context,
57                                                 krb5_principal *salt_princ)
58 {
59         krb5_error_code ret;
60         char *machine_username;
61         char *salt_body;
62         char *lower_realm;
63         struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
64         if (!mem_ctx) {
65                 return ENOMEM;
66         }
67         
68         machine_username = talloc_strdup(mem_ctx, cli_credentials_get_username(machine_account));
69
70         if (!machine_username) {
71                 talloc_free(mem_ctx);
72                 return ENOMEM;
73         }
74
75         if (machine_username[strlen(machine_username)-1] == '$') {
76                 machine_username[strlen(machine_username)-1] = '\0';
77         }
78         lower_realm = strlower_talloc(mem_ctx, cli_credentials_get_realm(machine_account));
79         if (!lower_realm) {
80                 talloc_free(mem_ctx);
81                 return ENOMEM;
82         }
83
84         salt_body = talloc_asprintf(mem_ctx, "%s.%s", machine_username, 
85                                     lower_realm);
86         if (!salt_body) {
87                 talloc_free(mem_ctx);
88                 return ENOMEM;
89         }
90         
91         ret = krb5_make_principal(smb_krb5_context->krb5_context, salt_princ, 
92                                   cli_credentials_get_realm(machine_account), 
93                                   "host", salt_body, NULL);
94
95         if (ret != 0) {
96                 mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
97                 mem_ctx->principal = *salt_princ;
98                 talloc_set_destructor(mem_ctx, free_principal);
99         }
100         return ret;
101 }
102
103 static int free_ccache(void *ptr) {
104         struct ccache_container *ccc = ptr;
105         /* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */
106         krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
107
108         return 0;
109 }
110
111 /**
112  * Return a freshly allocated ccache (destroyed by destructor on child
113  * of parent_ctx), for a given set of client credentials 
114  */
115
116  NTSTATUS kinit_to_ccache(TALLOC_CTX *parent_ctx,
117                           struct cli_credentials *credentials,
118                           struct smb_krb5_context *smb_krb5_context,
119                           krb5_ccache *ccache,
120                           const char **ccache_name) 
121 {
122         krb5_error_code ret;
123         const char *password;
124         char *ccache_string;
125         time_t kdc_time = 0;
126         struct ccache_container *mem_ctx = talloc(parent_ctx, struct ccache_container);
127
128         if (!mem_ctx) {
129                 return NT_STATUS_NO_MEMORY;
130         }
131
132         password = cli_credentials_get_password(credentials);
133         
134         /* this string should be unique */
135         ccache_string = talloc_asprintf(mem_ctx, "MEMORY:%s_%s", 
136                                         cli_credentials_get_principal(credentials, mem_ctx), 
137                                         generate_random_str(mem_ctx, 16));
138         
139         ret = krb5_cc_resolve(smb_krb5_context->krb5_context, ccache_string, ccache);
140         if (ret) {
141                 DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n", 
142                          ccache_string,
143                          error_message(ret)));
144                 talloc_free(mem_ctx);
145                 return NT_STATUS_INTERNAL_ERROR;
146         }
147
148         mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
149         mem_ctx->ccache = *ccache;
150
151         talloc_set_destructor(mem_ctx, free_ccache);
152         ret = kerberos_kinit_password_cc(smb_krb5_context->krb5_context, *ccache, 
153                                          cli_credentials_get_principal(credentials, mem_ctx), 
154                                          password, NULL, &kdc_time);
155         
156         /* cope with ticket being in the future due to clock skew */
157         if ((unsigned)kdc_time > time(NULL)) {
158                 time_t t = time(NULL);
159                 int time_offset =(unsigned)kdc_time-t;
160                 DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
161                 krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
162         }
163         
164         if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
165                 DEBUG(1,("kinit for %s failed (%s)\n", 
166                          cli_credentials_get_principal(credentials, mem_ctx), 
167                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
168                                                     ret, mem_ctx)));
169                 talloc_free(mem_ctx);
170                 return NT_STATUS_TIME_DIFFERENCE_AT_DC;
171         }
172         if (ret) {
173                 DEBUG(1,("kinit for %s failed (%s)\n", 
174                          cli_credentials_get_principal(credentials, mem_ctx), 
175                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
176                                                     ret, mem_ctx)));
177                 talloc_free(mem_ctx);
178                 return NT_STATUS_WRONG_PASSWORD;
179         } 
180         *ccache_name = ccache_string;
181
182         return NT_STATUS_OK;
183 }
184
185 static int free_keytab(void *ptr) {
186         struct keytab_container *ktc = ptr;
187         krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab);
188
189         return 0;
190 }
191
192  NTSTATUS create_memory_keytab(TALLOC_CTX *parent_ctx,
193                                struct cli_credentials *machine_account,
194                                struct smb_krb5_context *smb_krb5_context,
195                                krb5_keytab *keytab) 
196 {
197         krb5_error_code ret;
198         const char *password_s;
199         krb5_data password;
200         int i;
201         struct keytab_container *mem_ctx = talloc(parent_ctx, struct keytab_container);
202         krb5_enctype *enctypes;
203         krb5_principal salt_princ;
204         
205         if (!mem_ctx) {
206                 return NT_STATUS_NO_MEMORY;
207         }
208
209         password_s = talloc_strdup(mem_ctx, cli_credentials_get_password(machine_account));
210         if (!password_s) {
211                 DEBUG(1, ("create_memory_keytab: Could not obtain password for our local machine account!\n"));
212                 talloc_free(mem_ctx);
213                 return NT_STATUS_NO_MEMORY;
214         }
215         password.data = password_s;
216         password.length = strlen(password_s);
217         
218         /* this string should be unique */
219         
220         ret = krb5_kt_resolve(smb_krb5_context->krb5_context, "MEMORY_WILDCARD:", keytab);
221         if (ret) {
222                 DEBUG(1,("failed to generate a new krb5 keytab: %s\n", 
223                          error_message(ret)));
224                 talloc_free(mem_ctx);
225                 return NT_STATUS_INTERNAL_ERROR;
226         }
227
228         mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
229         mem_ctx->keytab = *keytab;
230
231         talloc_set_destructor(mem_ctx, free_keytab);
232
233         ret = salt_principal_from_credentials(mem_ctx, machine_account, 
234                                               smb_krb5_context, 
235                                               &salt_princ);
236         if (ret) {
237                 DEBUG(1,("create_memory_keytab: maksing salt principal failed (%s)\n",
238                          error_message(ret)));
239                 return NT_STATUS_INTERNAL_ERROR;
240         }
241
242         ret = get_kerberos_allowed_etypes(smb_krb5_context->krb5_context, 
243                                           &enctypes);
244         if (ret) {
245                 DEBUG(1,("create_memory_keytab: getting encrption types failed (%s)\n",
246                          error_message(ret)));
247                 return NT_STATUS_INTERNAL_ERROR;
248         }
249
250         for (i=0; enctypes[i]; i++) {
251                 krb5_keytab_entry entry;
252                 ret = create_kerberos_key_from_string(smb_krb5_context->krb5_context, 
253                                                       salt_princ, &password, &entry.keyblock, enctypes[i]);
254                 if (ret) {
255                         return NT_STATUS_INTERNAL_ERROR;
256                 }
257
258                 entry.principal = salt_princ;
259                 entry.vno       = 0 /* replace with real kvno */;
260                 ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, *keytab, &entry);
261                 if (ret) {
262                         DEBUG(1, ("Failed to add entry for %s to keytab: %s",
263                                   cli_credentials_get_principal(machine_account, mem_ctx), 
264                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
265                                                              ret, mem_ctx)));
266                         return NT_STATUS_INTERNAL_ERROR;
267                 }
268                 
269                 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
270         }
271
272         free_kerberos_etypes(smb_krb5_context->krb5_context, enctypes);
273
274         return NT_STATUS_OK;
275 }