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