r23801: The FSF has moved around a lot. This fixes their Mass Ave address.
[sfrench/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, 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                 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         free(base64_ticket);
67         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         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_advanced(SNUM(conn), conn->user,
226                               conn->connectpath, conn->gid,
227                               get_current_username(),
228                               current_user_info.domain,
229                               afs_username, sizeof(afs_username));
230
231         user_sid = &current_user.nt_user_token->user_sids[0];
232         pstring_sub(afs_username, "%s", sid_string_static(user_sid));
233
234         /* The pts command always generates completely lower-case user
235          * names. */
236         strlower_m(afs_username);
237
238         cell = strchr(afs_username, '@');
239
240         if (cell == NULL) {
241                 DEBUG(1, ("AFS username doesn't contain a @, "
242                           "could not find cell\n"));
243                 return False;
244         }
245
246         *cell = '\0';
247         cell += 1;
248
249         DEBUG(10, ("Trying to log into AFS for user %s@%s\n", 
250                    afs_username, cell));
251
252         if (!afs_createtoken(afs_username, cell, &ticket, &ct))
253                 return False;
254
255         /* For which Unix-UID do we want to set the token? */
256         ct.ViceId = getuid();
257
258         ticket_str = afs_encode_token(cell, ticket, &ct);
259
260         result = afs_settoken_str(ticket_str);
261
262         SAFE_FREE(ticket_str);
263
264         data_blob_free(&ticket);
265
266         return result;
267 }
268
269 #else
270
271 BOOL afs_login(connection_struct *conn)
272 {
273         return True;
274 }
275
276 char *afs_createtoken_str(const char *username, const char *cell)
277 {
278         return False;
279 }
280
281 #endif /* WITH_FAKE_KASERVER */