waf: add --with-fake-kaserver option
[samba.git] / lib / afs / afs_funcs.c
1 /*
2  *  Unix SMB/CIFS implementation.
3  *  Generate AFS tickets
4  *  Copyright (C) Volker Lendecke 2003
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "includes.h"
21 #include "lib/afs/afs_funcs.h"
22
23 #ifdef WITH_FAKE_KASERVER
24
25 #define NO_ASN1_TYPEDEFS 1
26
27 #include "secrets.h"
28 #include "passdb.h"
29 #include "auth.h"
30 #include "../librpc/gen_ndr/ndr_netlogon.h"
31 #include "lib/afs/afs_settoken.h"
32
33 #include <afs/param.h>
34 #include <afs/stds.h>
35 #include <afs/auth.h>
36 #include <afs/venus.h>
37 #include <asm/unistd.h>
38 #include <openssl/des.h>
39
40 struct ClearToken {
41         uint32 AuthHandle;
42         char HandShakeKey[8];
43         uint32 ViceId;
44         uint32 BeginTimestamp;
45         uint32 EndTimestamp;
46 };
47
48 static char *afs_encode_token(const char *cell, const DATA_BLOB ticket,
49                               const struct ClearToken *ct)
50 {
51         char *base64_ticket;
52         char *result = NULL;
53
54         DATA_BLOB key = data_blob(ct->HandShakeKey, 8);
55         char *base64_key;
56         TALLOC_CTX *mem_ctx;
57
58         mem_ctx = talloc_stackframe();
59         if (mem_ctx == NULL)
60                 goto done;
61
62         base64_ticket = base64_encode_data_blob(mem_ctx, ticket);
63         if (base64_ticket == NULL)
64                 goto done;
65
66         base64_key = base64_encode_data_blob(mem_ctx, key);
67         if (base64_key == NULL)
68                 goto done;
69
70         asprintf(&result, "%s\n%u\n%s\n%u\n%u\n%u\n%s\n", cell,
71                  ct->AuthHandle, base64_key, ct->ViceId, ct->BeginTimestamp,
72                  ct->EndTimestamp, base64_ticket);
73
74         DEBUG(10, ("Got ticket string:\n%s\n", result));
75
76 done:
77         TALLOC_FREE(mem_ctx);
78
79         return result;
80 }
81
82 /* Create a ClearToken and an encrypted ticket. ClearToken has not yet the
83  * ViceId set, this should be set by the caller. */
84
85 static bool afs_createtoken(const char *username, const char *cell,
86                             DATA_BLOB *ticket, struct ClearToken *ct)
87 {
88         fstring clear_ticket;
89         char *p = clear_ticket;
90         uint32 len;
91         uint32 now;
92
93         struct afs_key key;
94         des_key_schedule key_schedule;
95
96         if (!secrets_init())
97                 return false;
98
99         if (!secrets_fetch_afs_key(cell, &key)) {
100                 DEBUG(1, ("Could not fetch AFS service key\n"));
101                 return false;
102         }
103
104         ct->AuthHandle = key.kvno;
105
106         /* Build the ticket. This is going to be encrypted, so in our
107            way we fill in ct while we still have the unencrypted
108            form. */
109
110         p = clear_ticket;
111
112         /* The byte-order */
113         *p = 1;
114         p += 1;
115
116         /* "Alice", the client username */
117         strncpy(p, username, sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
118         p += strlen(p)+1;
119         strncpy(p, "", sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
120         p += strlen(p)+1;
121         strncpy(p, cell, sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
122         p += strlen(p)+1;
123
124         /* Alice's network layer address. At least Openafs-1.2.10
125            ignores this, so we fill in a dummy value here. */
126         SIVAL(p, 0, 0);
127         p += 4;
128
129         /* We need to create a session key */
130         generate_random_buffer((uint8_t *)p, 8);
131
132         /* Our client code needs the the key in the clear, it does not
133            know the server-key ... */
134         memcpy(ct->HandShakeKey, p, 8);
135
136         p += 8;
137
138         /* This is a kerberos 4 life time. The life time is expressed
139          * in units of 5 minute intervals up to 38400 seconds, after
140          * that a table is used up to lifetime 0xBF. Values between
141          * 0xC0 and 0xFF is undefined. 0xFF is defined to be the
142          * infinite time that never expire.
143          *
144          * So here we cheat and use the infinite time */
145         *p = 255;
146         p += 1;
147
148         /* Ticket creation time */
149         now = time(NULL);
150         SIVAL(p, 0, now);
151         ct->BeginTimestamp = now;
152
153         if(lp_afs_token_lifetime() == 0)
154                 ct->EndTimestamp = NEVERDATE;
155         else
156                 ct->EndTimestamp = now + lp_afs_token_lifetime();
157
158         if (((ct->EndTimestamp - ct->BeginTimestamp) & 1) == 1) {
159                 ct->BeginTimestamp += 1; /* Lifetime must be even */
160         }
161         p += 4;
162
163         /* And here comes Bob's name and instance, in this case the
164            AFS server. */
165         strncpy(p, "afs", sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
166         p += strlen(p)+1;
167         strncpy(p, "", sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
168         p += strlen(p)+1;
169
170         /* And zero-pad to a multiple of 8 bytes */
171         len = PTR_DIFF(p, clear_ticket);
172         if (len & 7) {
173                 uint32 extra_space = 8-(len & 7);
174                 memset(p, 0, extra_space);
175                 p+=extra_space;
176         }
177         len = PTR_DIFF(p, clear_ticket);
178
179         des_key_sched((const_des_cblock *)key.key, key_schedule);
180         des_pcbc_encrypt((const unsigned char*) clear_ticket,
181                          (unsigned char*) clear_ticket,
182                          len, key_schedule, (C_Block *)key.key, 1);
183
184         ZERO_STRUCT(key);
185
186         *ticket = data_blob(clear_ticket, len);
187
188         return true;
189 }
190
191 char *afs_createtoken_str(const char *username, const char *cell)
192 {
193         DATA_BLOB ticket;
194         struct ClearToken ct;
195         char *result;
196
197         if (!afs_createtoken(username, cell, &ticket, &ct))
198                 return NULL;
199
200         result = afs_encode_token(cell, ticket, &ct);
201
202         data_blob_free(&ticket);
203
204         return result;
205 }
206
207 /*
208   This routine takes a radical approach completely bypassing the
209   Kerberos idea of security and using AFS simply as an intelligent
210   file backend. Samba has persuaded itself somehow that the user is
211   actually correctly identified and then we create a ticket that the
212   AFS server hopefully accepts using its KeyFile that the admin has
213   kindly stored to our secrets.tdb.
214
215   Thanks to the book "Network Security -- PRIVATE Communication in a
216   PUBLIC World" by Charlie Kaufman, Radia Perlman and Mike Speciner
217   Kerberos 4 tickets are not really hard to construct.
218
219   For the comments "Alice" is the User to be auth'ed, and "Bob" is the
220   AFS server.  */
221
222 bool afs_login(connection_struct *conn)
223 {
224         DATA_BLOB ticket;
225         char *afs_username = NULL;
226         char *cell = NULL;
227         bool result;
228         char *ticket_str = NULL;
229         const struct dom_sid *user_sid;
230         TALLOC_CTX *ctx = talloc_tos();
231
232         struct ClearToken ct;
233
234         afs_username = talloc_strdup(ctx,
235                                 lp_afs_username_map());
236         if (!afs_username) {
237                 return false;
238         }
239
240         afs_username = talloc_sub_advanced(ctx,
241                                 lp_servicename(ctx, SNUM(conn)),
242                                 conn->session_info->unix_info->unix_name,
243                                 conn->connectpath,
244                                 conn->session_info->unix_token->gid,
245                                 conn->session_info->unix_info->sanitized_username,
246                                 conn->session_info->info->domain_name,
247                                 afs_username);
248         if (!afs_username) {
249                 return false;
250         }
251
252         user_sid = &conn->session_info->security_token->sids[0];
253         afs_username = talloc_string_sub(talloc_tos(),
254                                         afs_username,
255                                         "%s",
256                                         sid_string_tos(user_sid));
257         if (!afs_username) {
258                 return false;
259         }
260
261         /* The pts command always generates completely lower-case user
262          * names. */
263         if (!strlower_m(afs_username)) {
264                 return false;
265         }
266
267         cell = strchr(afs_username, '@');
268
269         if (cell == NULL) {
270                 DEBUG(1, ("AFS username doesn't contain a @, "
271                           "could not find cell\n"));
272                 return false;
273         }
274
275         *cell = '\0';
276         cell += 1;
277
278         DEBUG(10, ("Trying to log into AFS for user %s@%s\n",
279                    afs_username, cell));
280
281         if (!afs_createtoken(afs_username, cell, &ticket, &ct))
282                 return false;
283
284         /* For which Unix-UID do we want to set the token? */
285         ct.ViceId = getuid();
286
287         ticket_str = afs_encode_token(cell, ticket, &ct);
288
289         result = afs_settoken_str(ticket_str);
290
291         SAFE_FREE(ticket_str);
292
293         data_blob_free(&ticket);
294
295         return result;
296 }
297
298 #else
299
300 bool afs_login(connection_struct *conn)
301 {
302         return true;
303 }
304
305 char *afs_createtoken_str(const char *username, const char *cell)
306 {
307         return NULL;
308 }
309
310 #endif /* WITH_FAKE_KASERVER */