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