added internal sasl/gssapi code. This means we are no longer dependent on cyrus-sasl...
[kai/samba.git] / source3 / libads / kerberos.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    kerberos utility library
5    Copyright (C) Andrew Tridgell 2001
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 #ifdef HAVE_KRB5
25
26 /*
27   simulate a kinit, putting the tgt in the default cache location
28   remus@snapserver.com
29 */
30 int kerberos_kinit_password(const char *principal, const char *password)
31 {
32         krb5_context ctx;
33         krb5_error_code code = 0;
34         krb5_ccache cc;
35         krb5_principal me;
36         krb5_creds my_creds;
37         
38         if ((code = krb5_init_context(&ctx)))
39                 return code;
40         
41         if ((code = krb5_cc_default(ctx, &cc))) {
42                 krb5_free_context(ctx);
43                 return code;
44         }
45         
46         if ((code = krb5_parse_name(ctx, principal, &me))) {
47                 krb5_free_context(ctx); 
48                 return code;
49         }
50         
51         if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, password, NULL, 
52                                                 NULL, 0, NULL, NULL))) {
53                 krb5_free_principal(ctx, me);
54                 krb5_free_context(ctx);         
55                 return code;
56         }
57         
58         if ((code = krb5_cc_initialize(ctx, cc, me))) {
59                 krb5_free_cred_contents(ctx, &my_creds);
60                 krb5_free_principal(ctx, me);
61                 krb5_free_context(ctx);         
62                 return code;
63         }
64         
65         if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
66                 krb5_cc_close(ctx, cc);
67                 krb5_free_cred_contents(ctx, &my_creds);
68                 krb5_free_principal(ctx, me);
69                 krb5_free_context(ctx);         
70                 return code;
71         }
72         
73         krb5_cc_close(ctx, cc);
74         krb5_free_cred_contents(ctx, &my_creds);
75         krb5_free_principal(ctx, me);
76         krb5_free_context(ctx);         
77         
78         return 0;
79 }
80
81
82
83 /* run kinit to setup our ccache */
84 int ads_kinit_password(ADS_STRUCT *ads)
85 {
86         char *s;
87         int ret;
88         char *ccache;
89
90         ccache = lock_path("winbindd_ccache");
91
92         /* we don't want this to affect the users ccache */
93         setenv("KRB5CCNAME", ccache, 1);
94
95         unlink(ccache);
96
97         if (!ads->user_name) {
98                 /* by default use the machine account */
99                 extern pstring global_myname;
100                 fstring myname;
101                 fstrcpy(myname, global_myname);
102                 strlower(myname);
103                 asprintf(&ads->user_name, "HOST/%s", global_myname);
104         }
105         asprintf(&s, "%s@%s", ads->user_name, ads->realm);
106         ret = kerberos_kinit_password(s, ads->password);
107         free(s);
108         if (ret) {
109                 DEBUG(1,("kerberos_kinit_password %s failed: %s\n", 
110                          s, error_message(ret)));
111         }
112         return ret;
113 }
114
115 /*
116   verify an incoming ticket and parse out the principal name and 
117   authorization_data if available 
118 */
119 NTSTATUS ads_verify_ticket(ADS_STRUCT *ads, const DATA_BLOB *ticket, 
120                            char **principal, DATA_BLOB *auth_data)
121 {
122         krb5_context context;
123         krb5_auth_context auth_context = NULL;
124         krb5_keytab keytab = NULL;
125         krb5_data packet;
126         krb5_ticket *tkt = NULL;
127         krb5_data salt;
128         krb5_encrypt_block eblock;
129         int ret;
130         krb5_keyblock * key;
131         krb5_principal host_princ;
132         char *host_princ_s;
133         extern pstring global_myname;
134         fstring myname;
135         char *password_s;
136         krb5_data password;
137
138         if (!secrets_init()) {
139                 DEBUG(1,("secrets_init failed\n"));
140                 return NT_STATUS_LOGON_FAILURE;
141         }
142
143         password_s = secrets_fetch_machine_password();
144         if (!password_s) {
145                 DEBUG(1,("failed to fetch machine password\n"));
146                 return NT_STATUS_LOGON_FAILURE;
147         }
148
149         password.data = password_s;
150         password.length = strlen(password_s);
151
152         ret = krb5_init_context(&context);
153         if (ret) {
154                 DEBUG(1,("krb5_init_context failed (%s)\n", error_message(ret)));
155                 return NT_STATUS_LOGON_FAILURE;
156         }
157
158         ret = krb5_set_default_realm(context, ads->realm);
159         if (ret) {
160                 DEBUG(1,("krb5_set_default_realm failed (%s)\n", error_message(ret)));
161                 ads_destroy(&ads);
162                 return NT_STATUS_LOGON_FAILURE;
163         }
164
165         /* this whole process is far more complex than I would
166            like. We have to go through all this to allow us to store
167            the secret internally, instead of using /etc/krb5.keytab */
168         ret = krb5_auth_con_init(context, &auth_context);
169         if (ret) {
170                 DEBUG(1,("krb5_auth_con_init failed (%s)\n", error_message(ret)));
171                 return NT_STATUS_LOGON_FAILURE;
172         }
173
174         fstrcpy(myname, global_myname);
175         strlower(myname);
176         asprintf(&host_princ_s, "HOST/%s@%s", myname, lp_realm());
177         ret = krb5_parse_name(context, host_princ_s, &host_princ);
178         if (ret) {
179                 DEBUG(1,("krb5_parse_name(%s) failed (%s)\n", host_princ_s, error_message(ret)));
180                 return NT_STATUS_LOGON_FAILURE;
181         }
182
183         ret = krb5_principal2salt(context, host_princ, &salt);
184         if (ret) {
185                 DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret)));
186                 return NT_STATUS_LOGON_FAILURE;
187         }
188     
189         if (!(key = (krb5_keyblock *)malloc(sizeof(*key)))) {
190                 return NT_STATUS_NO_MEMORY;
191         }
192         
193         krb5_use_enctype(context, &eblock, ENCTYPE_DES_CBC_MD5);
194         
195         ret = krb5_string_to_key(context, &eblock, key, &password, &salt);
196         if (ret) {
197                 DEBUG(1,("krb5_string_to_key failed (%s)\n", error_message(ret)));
198                 return NT_STATUS_LOGON_FAILURE;
199         }
200
201         krb5_auth_con_setuseruserkey(context, auth_context, key);
202
203         packet.length = ticket->length;
204         packet.data = (krb5_pointer)ticket->data;
205
206 #if 0
207         file_save("/tmp/ticket.dat", ticket->data, ticket->length);
208 #endif
209
210         if ((ret = krb5_rd_req(context, &auth_context, &packet, 
211                                NULL, keytab, NULL, &tkt))) {
212                 DEBUG(3,("krb5_rd_req with auth failed (%s)\n", 
213                          error_message(ret)));
214                 return NT_STATUS_LOGON_FAILURE;
215         }
216
217         if (tkt->enc_part2) {
218                 *auth_data = data_blob(tkt->enc_part2->authorization_data[0]->contents,
219                                        tkt->enc_part2->authorization_data[0]->length);
220         }
221
222 #if 0
223         if (tkt->enc_part2) {
224                 file_save("/tmp/authdata.dat", 
225                           tkt->enc_part2->authorization_data[0]->contents,
226                           tkt->enc_part2->authorization_data[0]->length);
227         }
228 #endif
229
230         if ((ret = krb5_unparse_name(context, tkt->enc_part2->client, principal))) {
231                 DEBUG(3,("krb5_unparse_name failed (%s)\n", 
232                          error_message(ret)));
233                 return NT_STATUS_LOGON_FAILURE;
234         }
235
236         return NT_STATUS_OK;
237 }
238
239 #endif