merge from 3.0...LDAP password lockout support
[tprouty/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         /* The pts command always generates completely lower-case user
146          * names. */
147         strlower_m(afs_username);
148
149         cell = strchr(afs_username, '@');
150
151         if (cell == NULL) {
152                 DEBUG(1, ("AFS username doesn't contain a @, "
153                           "could not find cell\n"));
154                 return False;
155         }
156
157         *cell = '\0';
158         cell += 1;
159
160         DEBUG(10, ("Trying to log into AFS for user %s@%s\n", 
161                    afs_username, cell));
162
163         if (!secrets_init()) 
164                 return False;
165
166         if (!secrets_fetch_afs_key(cell, &key)) {
167                 DEBUG(5, ("Could not fetch AFS service key\n"));
168                 return False;
169         }
170
171         ct.AuthHandle = key.kvno;
172
173         /* Build the ticket. This is going to be encrypted, so in our
174            way we fill in ct while we still have the unencrypted
175            form. */
176
177         p = ticket;
178
179         /* The byte-order */
180         *p = 1;
181         p += 1;
182
183         /* "Alice", the client username */
184         strncpy(p, afs_username, sizeof(ticket)-PTR_DIFF(p,ticket)-1);
185         p += strlen(p)+1;
186         strncpy(p, "", sizeof(ticket)-PTR_DIFF(p,ticket)-1);
187         p += strlen(p)+1;
188         strncpy(p, cell, sizeof(ticket)-PTR_DIFF(p,ticket)-1);
189         p += strlen(p)+1;
190
191         /* This assumes that we have setresuid and set the real uid as well as
192            the effective uid in set_effective_uid(). */
193         ct.ViceId = getuid();
194         DEBUG(10, ("Creating Token for uid %d\n", ct.ViceId));
195
196         /* Alice's network layer address. At least Openafs-1.2.10
197            ignores this, so we fill in a dummy value here. */
198         SIVAL(p, 0, 0);
199         p += 4;
200
201         /* We need to create a session key */
202         generate_random_buffer(p, 8, False);
203
204         /* Our client code needs the the key in the clear, it does not
205            know the server-key ... */
206         memcpy(ct.HandShakeKey, p, 8);
207
208         p += 8;
209
210         /* Ticket lifetime. We fake everything here, so go as long as
211            possible. This is in 5-minute intervals, so 255 is 21 hours
212            and 15 minutes.*/
213         *p = 255;
214         p += 1;
215
216         /* Ticket creation time */
217         now = time(NULL);
218         SIVAL(p, 0, now);
219         ct.BeginTimestamp = now;
220
221         ct.EndTimestamp = now + (255*60*5);
222         if (((ct.EndTimestamp - ct.BeginTimestamp) & 1) == 1) {
223                 ct.BeginTimestamp += 1; /* Lifetime must be even */
224         }
225         p += 4;
226
227         /* And here comes Bob's name and instance, in this case the
228            AFS server. */
229         strncpy(p, "afs", sizeof(ticket)-PTR_DIFF(p,ticket)-1);
230         p += strlen(p)+1;
231         strncpy(p, "", sizeof(ticket)-PTR_DIFF(p,ticket)-1);
232         p += strlen(p)+1;
233
234         /* And zero-pad to a multiple of 8 bytes */
235         len = PTR_DIFF(p, ticket);
236         if (len & 7) {
237                 uint32 extra_space = 8-(len & 7);
238                 memset(p, 0, extra_space);
239                 p+=extra_space;
240         }
241         len = PTR_DIFF(p, ticket);
242
243         des_key_sched((const_des_cblock *)key.key, key_schedule);
244         des_pcbc_encrypt(ticket, ticket,
245                          len, key_schedule, (C_Block *)key.key, 1);
246
247         ZERO_STRUCT(key);
248
249         return afs_settoken(afs_username, cell, &ct, ticket, len);
250 }
251
252 #else
253
254 BOOL afs_login(connection_struct *conn)
255 {
256         return True;
257 }
258
259 #endif /* WITH_FAKE_KASERVER */