Correctly check for errors in strlower_m() returns.
[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((uint8_t *)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((const unsigned char*) clear_ticket,
180                          (unsigned char*) clear_ticket,
181                          len, key_schedule, (C_Block *)key.key, 1);
182
183         ZERO_STRUCT(key);
184
185         *ticket = data_blob(clear_ticket, len);
186
187         return true;
188 }
189
190 char *afs_createtoken_str(const char *username, const char *cell)
191 {
192         DATA_BLOB ticket;
193         struct ClearToken ct;
194         char *result;
195
196         if (!afs_createtoken(username, cell, &ticket, &ct))
197                 return NULL;
198
199         result = afs_encode_token(cell, ticket, &ct);
200
201         data_blob_free(&ticket);
202
203         return result;
204 }
205
206 /*
207   This routine takes a radical approach completely bypassing the
208   Kerberos idea of security and using AFS simply as an intelligent
209   file backend. Samba has persuaded itself somehow that the user is
210   actually correctly identified and then we create a ticket that the
211   AFS server hopefully accepts using its KeyFile that the admin has
212   kindly stored to our secrets.tdb.
213
214   Thanks to the book "Network Security -- PRIVATE Communication in a
215   PUBLIC World" by Charlie Kaufman, Radia Perlman and Mike Speciner
216   Kerberos 4 tickets are not really hard to construct.
217
218   For the comments "Alice" is the User to be auth'ed, and "Bob" is the
219   AFS server.  */
220
221 bool afs_login(connection_struct *conn)
222 {
223         DATA_BLOB ticket;
224         char *afs_username = NULL;
225         char *cell = NULL;
226         bool result;
227         char *ticket_str = NULL;
228         const struct dom_sid *user_sid;
229         TALLOC_CTX *ctx = talloc_tos();
230
231         struct ClearToken ct;
232
233         afs_username = talloc_strdup(ctx,
234                                 lp_afs_username_map());
235         if (!afs_username) {
236                 return false;
237         }
238
239         afs_username = talloc_sub_advanced(ctx,
240                                 lp_servicename(SNUM(conn)),
241                                 conn->session_info->unix_info->unix_name,
242                                 conn->connectpath,
243                                 conn->session_info->unix_token->gid,
244                                 conn->session_info->unix_info->sanitized_username,
245                                 conn->session_info->info->domain_name,
246                                 afs_username);
247         if (!afs_username) {
248                 return false;
249         }
250
251         user_sid = &conn->session_info->security_token->sids[0];
252         afs_username = talloc_string_sub(talloc_tos(),
253                                         afs_username,
254                                         "%s",
255                                         sid_string_tos(user_sid));
256         if (!afs_username) {
257                 return false;
258         }
259
260         /* The pts command always generates completely lower-case user
261          * names. */
262         if (!strlower_m(afs_username)) {
263                 return false;
264         }
265
266         cell = strchr(afs_username, '@');
267
268         if (cell == NULL) {
269                 DEBUG(1, ("AFS username doesn't contain a @, "
270                           "could not find cell\n"));
271                 return false;
272         }
273
274         *cell = '\0';
275         cell += 1;
276
277         DEBUG(10, ("Trying to log into AFS for user %s@%s\n",
278                    afs_username, cell));
279
280         if (!afs_createtoken(afs_username, cell, &ticket, &ct))
281                 return false;
282
283         /* For which Unix-UID do we want to set the token? */
284         ct.ViceId = getuid();
285
286         ticket_str = afs_encode_token(cell, ticket, &ct);
287
288         result = afs_settoken_str(ticket_str);
289
290         SAFE_FREE(ticket_str);
291
292         data_blob_free(&ticket);
293
294         return result;
295 }
296
297 #else
298
299 bool afs_login(connection_struct *conn)
300 {
301         return true;
302 }
303
304 char *afs_createtoken_str(const char *username, const char *cell)
305 {
306         return NULL;
307 }
308
309 #endif /* WITH_FAKE_KASERVER */