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