Merge from 3_0:
[gd/samba/.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 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 #include <afs/stds.h>
26 #include <afs/afs.h>
27 #include <afs/auth.h>
28 #include <afs/venus.h>
29 #include <asm/unistd.h>
30 #include <openssl/des.h>
31
32 _syscall5(int, afs_syscall, int, subcall,
33           char *, path,
34           int, cmd,
35           char *, cmarg,
36           int, follow);
37
38 struct ClearToken {
39         uint32 AuthHandle;
40         char HandShakeKey[8];
41         uint32 ViceId;
42         uint32 BeginTimestamp;
43         uint32 EndTimestamp;
44 };
45
46 /*
47   Put an AFS token into the Kernel so that it can authenticate against
48   the AFS server. This assumes correct local uid settings.
49
50   This is currently highly Linux and OpenAFS-specific. The correct API
51   call for this would be ktc_SetToken. But to do that we would have to
52   import a REALLY big bunch of libraries which I would currently like
53   to avoid. 
54 */
55
56 static BOOL afs_settoken(const char *username, const char *cell,
57                          const struct ClearToken *ctok,
58                          char *v4tkt_data, int v4tkt_length)
59 {
60         int ret;
61         struct {
62                 char *in, *out;
63                 uint16 in_size, out_size;
64         } iob;
65
66         char buf[1024];
67         char *p = buf;
68         int tmp;
69
70         memcpy(p, &v4tkt_length, sizeof(uint32));
71         p += sizeof(uint32);
72         memcpy(p, v4tkt_data, v4tkt_length);
73         p += v4tkt_length;
74
75         tmp = sizeof(struct ClearToken);
76         memcpy(p, &tmp, sizeof(uint32));
77         p += sizeof(uint32);
78         memcpy(p, ctok, tmp);
79         p += tmp;
80
81         tmp = 0;
82
83         memcpy(p, &tmp, sizeof(uint32));
84         p += sizeof(uint32);
85
86         tmp = strlen(cell);
87         if (tmp >= MAXKTCREALMLEN) {
88                 DEBUG(1, ("Realm too long\n"));
89                 return False;
90         }
91
92         strncpy(p, cell, tmp);
93         p += tmp;
94         *p = 0;
95         p +=1;
96
97         iob.in = buf;
98         iob.in_size = PTR_DIFF(p,buf);
99         iob.out = buf;
100         iob.out_size = sizeof(buf);
101
102 #if 0
103         file_save("/tmp/ioctlbuf", iob.in, iob.in_size);
104 #endif
105
106         ret = afs_syscall(AFSCALL_PIOCTL, 0, VIOCSETTOK, (char *)&iob, 0);
107
108         DEBUG(10, ("afs VIOCSETTOK returned %d\n", ret));
109         return (ret == 0);
110 }
111
112 /*
113   This routine takes a radical approach completely defeating the
114   Kerberos idea of security and using AFS simply as an intelligent
115   file backend. Samba has persuaded itself somehow that the user is
116   actually correctly identified and then we create a ticket that the
117   AFS server hopefully accepts using its KeyFile that the admin has
118   kindly stored to our secrets.tdb.
119
120   Thanks to the book "Network Security -- PRIVATE Communication in a
121   PUBLIC World" by Charlie Kaufman, Radia Perlman and Mike Speciner
122   Kerberos 4 tickets are not really hard to construct.
123
124   For the comments "Alice" is the User to be auth'ed, and "Bob" is the
125   AFS server.  */
126
127 BOOL afs_login(connection_struct *conn)
128 {
129         fstring ticket;
130         char *p = ticket;
131         uint32 len;
132         struct afs_key key;
133         pstring afs_username;
134         char *cell;
135
136         struct ClearToken ct;
137
138         uint32 now;             /* I assume time() returns 32 bit */
139
140         des_key_schedule key_schedule;
141
142         pstrcpy(afs_username, lp_afs_username_map());
143         standard_sub_conn(conn, afs_username, sizeof(afs_username));
144
145         cell = strchr(afs_username, '@');
146
147         if (cell == NULL) {
148                 DEBUG(1, ("AFS username doesn't contain a @, "
149                           "could not find cell\n"));
150                 return False;
151         }
152
153         *cell = '\0';
154         cell += 1;
155         strlower_m(cell);
156
157         DEBUG(10, ("Trying to log into AFS for user %s@%s\n", 
158                    afs_username, cell));
159
160         if (!secrets_init()) 
161                 return False;
162
163         if (!secrets_fetch_afs_key(cell, &key)) {
164                 DEBUG(5, ("Could not fetch AFS service key\n"));
165                 return False;
166         }
167
168         ct.AuthHandle = key.kvno;
169
170         /* Build the ticket. This is going to be encrypted, so in our
171            way we fill in ct while we still have the unencrypted
172            form. */
173
174         p = ticket;
175
176         /* The byte-order */
177         *p = 1;
178         p += 1;
179
180         /* "Alice", the client username */
181         strncpy(p, afs_username, sizeof(ticket)-PTR_DIFF(p,ticket)-1);
182         p += strlen(p)+1;
183         strncpy(p, "", sizeof(ticket)-PTR_DIFF(p,ticket)-1);
184         p += strlen(p)+1;
185         strncpy(p, cell, sizeof(ticket)-PTR_DIFF(p,ticket)-1);
186         p += strlen(p)+1;
187
188         /* This assumes that we have setresuid and set the real uid as well as
189            the effective uid in set_effective_uid(). */
190         ct.ViceId = getuid();
191         DEBUG(10, ("Creating Token for uid %d\n", ct.ViceId));
192
193         /* Alice's network layer address. At least Openafs-1.2.10
194            ignores this, so we fill in a dummy value here. */
195         SIVAL(p, 0, 0);
196         p += 4;
197
198         /* We need to create a session key */
199         generate_random_buffer(p, 8, False);
200
201         /* Our client code needs the the key in the clear, it does not
202            know the server-key ... */
203         memcpy(ct.HandShakeKey, p, 8);
204
205         p += 8;
206
207         /* Ticket lifetime. We fake everything here, so go as long as
208            possible. This is in 5-minute intervals, so 255 is 21 hours
209            and 15 minutes.*/
210         *p = 255;
211         p += 1;
212
213         /* Ticket creation time */
214         now = time(NULL);
215         SIVAL(p, 0, now);
216         ct.BeginTimestamp = now;
217
218         ct.EndTimestamp = now + (255*60*5);
219         if (((ct.EndTimestamp - ct.BeginTimestamp) & 1) == 1) {
220                 ct.BeginTimestamp += 1; /* Lifetime must be even */
221         }
222         p += 4;
223
224         /* And here comes Bob's name and instance, in this case the
225            AFS server. */
226         strncpy(p, "afs", sizeof(ticket)-PTR_DIFF(p,ticket)-1);
227         p += strlen(p)+1;
228         strncpy(p, "", sizeof(ticket)-PTR_DIFF(p,ticket)-1);
229         p += strlen(p)+1;
230
231         /* And zero-pad to a multiple of 8 bytes */
232         len = PTR_DIFF(p, ticket);
233         if (len & 7) {
234                 uint32 extra_space = 8-(len & 7);
235                 memset(p, 0, extra_space);
236                 p+=extra_space;
237         }
238         len = PTR_DIFF(p, ticket);
239
240         des_key_sched((const_des_cblock *)key.key, key_schedule);
241         des_pcbc_encrypt(ticket, ticket,
242                          len, key_schedule, (C_Block *)key.key, 1);
243
244         ZERO_STRUCT(key);
245
246         return afs_settoken(afs_username, cell, &ct, ticket, len);
247 }
248
249 #else
250
251 BOOL afs_login(connection_struct *conn)
252 {
253         return True;
254 }
255
256 #endif /* WITH_FAKE_KASERVER */