version incremented
[samba] / 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 2 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 struct current_user current_user;
215         DATA_BLOB ticket;
216         pstring afs_username;
217         char *cell;
218         BOOL result;
219         char *ticket_str;
220         const DOM_SID *user_sid;
221
222         struct ClearToken ct;
223
224         pstrcpy(afs_username, lp_afs_username_map());
225         standard_sub_conn(conn, afs_username, sizeof(afs_username));
226
227         user_sid = &current_user.nt_user_token->user_sids[0];
228         pstring_sub(afs_username, "%s", sid_string_static(user_sid));
229
230         /* The pts command always generates completely lower-case user
231          * names. */
232         strlower_m(afs_username);
233
234         cell = strchr(afs_username, '@');
235
236         if (cell == NULL) {
237                 DEBUG(1, ("AFS username doesn't contain a @, "
238                           "could not find cell\n"));
239                 return False;
240         }
241
242         *cell = '\0';
243         cell += 1;
244
245         DEBUG(10, ("Trying to log into AFS for user %s@%s\n", 
246                    afs_username, cell));
247
248         if (!afs_createtoken(afs_username, cell, &ticket, &ct))
249                 return False;
250
251         /* For which Unix-UID do we want to set the token? */
252         ct.ViceId = getuid();
253
254         ticket_str = afs_encode_token(cell, ticket, &ct);
255
256         result = afs_settoken_str(ticket_str);
257
258         SAFE_FREE(ticket_str);
259
260         data_blob_free(&ticket);
261
262         return result;
263 }
264
265 #else
266
267 BOOL afs_login(connection_struct *conn)
268 {
269         return True;
270 }
271
272 char *afs_createtoken_str(const char *username, const char *cell)
273 {
274         return False;
275 }
276
277 #endif /* WITH_FAKE_KASERVER */