d14f5b87cba8f4ccd6db04fbfaf4d9582c789f3f
[openafs.git] / src / libafscp / afscp_util.c
1 /* AUTORIGHTS
2 Copyright (C) 2003 - 2010 Chaskiel Grundman
3 Copyright (c) 2011 Your Filesystem Inc.
4 Copyright (c) 2012 Sine Nomine Associates
5 All rights reserved
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10
11 1. Redistributions of source code must retain the above copyright
12    notice, this list of conditions and the following disclaimer.
13
14 2. Redistributions in binary form must reproduce the above copyright
15    notice, this list of conditions and the following disclaimer in the
16    documentation and/or other materials provided with the distribution.
17
18 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 #include <afsconfig.h>
30 #include <afs/param.h>
31
32 #include <roken.h>
33
34 #include <ctype.h>
35 #include <afs/cellconfig.h>
36 #ifndef AFSCONF_CLIENTNAME
37 #include <afs/dirpath.h>
38 #define AFSCONF_CLIENTNAME AFSDIR_CLIENT_ETC_DIRPATH
39 #endif
40 #include <ubik.h>
41 #include <rx/rx_null.h>
42 #include <rx/rxkad.h>
43 #ifdef HAVE_KERBEROS
44 # include <krb5.h>
45 #endif
46 #include "afscp.h"
47 #include "afscp_internal.h"
48
49 #define HC_DEPRECATED
50 #include <hcrypto/des.h>
51
52 #ifdef HAVE_KRB5_CREDS_KEYBLOCK_ENCTYPE
53 #define Z_keydata(keyblock)     ((keyblock)->contents)
54 #define Z_keylen(keyblock)      ((keyblock)->length)
55 #define Z_credskey(creds)       (&(creds)->keyblock)
56 #define Z_enctype(keyblock)     ((keyblock)->enctype)
57 #else
58 #define Z_keydata(keyblock)     ((keyblock)->keyvalue.data)
59 #define Z_keylen(keyblock)      ((keyblock)->keyvalue.length)
60 #define Z_credskey(creds)       (&(creds)->session)
61 #define Z_enctype(keyblock)     ((keyblock)->keytype)
62 #endif
63
64 static int insecure = 0;
65 static int try_anonymous = 0;
66 static char authas_name[256];
67 static char authas_inst[256];
68
69 int
70 afscp_Insecure(void)
71 {
72     insecure = 1;
73     return 0;
74 }
75
76 int
77 afscp_AnonymousAuth(int state)
78 {
79     try_anonymous = state;
80     return 0;
81 }
82
83 /**
84  * Connect to all servers using authenticated connections, using the local
85  * KeyFile to appear as an arbitrary user.
86  *
87  * @param[in] aname  The pts username to impersonate
88  *
89  * @note aname is krb4-based name, not a krb5 principal. So for example, you
90  *       probably want to give "user.admin" instead of "user/admin".
91  *
92  * @return operation status
93  *  @retval 0 success
94  */
95 int
96 afscp_LocalAuthAs(const char *aname)
97 {
98     const char *ainst = strchr(aname, '.');
99     size_t namelen, instlen;
100
101     if (ainst) {
102         namelen = ainst - aname;
103         ainst++;
104         instlen = strlen(ainst);
105     } else {
106         namelen = strlen(aname);
107         ainst = "";
108         instlen = 0;
109     }
110
111     if (namelen+1 > sizeof(authas_name) || instlen+1 > sizeof(authas_inst)) {
112         return EINVAL;
113     }
114     strncpy(authas_name, aname, namelen);
115     strncpy(authas_inst, ainst, instlen);
116     return 0;
117 }
118
119 static struct afsconf_dir *confdir;
120
121 void
122 afscp_SetConfDir(char *confDir)
123 {
124     if (confdir != NULL)
125         afsconf_Close(confdir);
126
127     confdir = afsconf_Open(confDir);
128 }
129
130 static int
131 _GetCellInfo(char *cell, struct afsconf_cell *celldata)
132 {
133     int code;
134     if (confdir == NULL)
135         confdir = afsconf_Open(AFSCONF_CLIENTNAME);
136     if (confdir == NULL) {
137         return AFSCONF_NODB;
138     }
139     code = afsconf_GetCellInfo(confdir, cell, AFSCONF_VLDBSERVICE, celldata);
140     return code;
141 }
142
143 static int
144 _GetNullSecurityObject(struct afscp_cell *cell)
145 {
146     cell->security = (struct rx_securityClass *)rxnull_NewClientSecurityObject();
147     cell->scindex = RX_SECIDX_NULL;
148     return 0;
149 }
150
151 static int
152 _GetLocalSecurityObject(struct afscp_cell *cell,
153                         char *aname, char *ainst)
154 {
155     int code = 0;
156     char tbuffer[256];
157     struct ktc_encryptionKey key, session;
158     struct rx_securityClass *tc;
159     afs_int32 kvno;
160     afs_int32 ticketLen;
161     rxkad_level lev;
162     struct afsconf_dir *tdir;
163
164     tdir = afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH);
165     if (!tdir) {
166         code = AFSCONF_FAILURE;
167         goto done;
168     }
169
170     code = afsconf_GetLatestKey(tdir, &kvno, &key);
171     if (code) {
172         goto done;
173     }
174
175     DES_init_random_number_generator((DES_cblock *)&key);
176     code = DES_new_random_key((DES_cblock *)&session);
177     if (code) {
178         goto done;
179     }
180
181     ticketLen = sizeof(tbuffer);
182     memset(tbuffer, 0, sizeof(tbuffer));
183     code = tkt_MakeTicket(tbuffer, &ticketLen, &key, aname, ainst, "", 0,
184                           0xffffffff, &session, 0, "afs", "");
185     if (code) {
186         goto done;
187     }
188
189     if (insecure) {
190         lev = rxkad_clear;
191     } else {
192         lev = rxkad_crypt;
193     }
194
195     tc = (struct rx_securityClass *)
196         rxkad_NewClientSecurityObject(lev, &session, kvno, ticketLen,
197                                       tbuffer);
198     if (!tc) {
199         code = RXKADBADKEY;
200         goto done;
201     }
202
203     cell->security = tc;
204     cell->scindex = 2;
205
206  done:
207     if (tdir) {
208         afsconf_Close(tdir);
209     }
210     return code;
211 }
212
213 int
214 _GetSecurityObject(struct afscp_cell *cell)
215 {
216     int code = ENOENT;
217 #ifdef HAVE_KERBEROS
218     krb5_context context;
219     krb5_creds match;
220     krb5_creds *cred;
221     krb5_ccache cc;
222     char **realms, *realm;
223     struct afsconf_cell celldata;
224     char localcell[MAXCELLCHARS + 1];
225     struct rx_securityClass *sc;
226     struct ktc_encryptionKey k;
227     int i;
228     rxkad_level l;
229     code = _GetCellInfo(cell->name, &celldata);
230     if (code != 0) {
231         goto try_anon;
232     }
233
234     if (authas_name[0]) {
235         code = _GetLocalSecurityObject(cell, authas_name, authas_inst);
236         if (code == 0) {
237             return 0;
238         }
239     }
240
241     code = krb5_init_context(&context); /* see aklog.c main() */
242     if (code != 0) {
243         goto try_anon;
244     }
245
246     if (cell->realm == NULL) {
247         realm = NULL;
248         code = krb5_get_host_realm(context, celldata.hostName[0], &realms);
249
250         if (code == 0) {
251             strlcpy(localcell, realms[0], sizeof(localcell));
252             krb5_free_host_realm(context, realms);
253             realm = localcell;
254         } else
255             goto try_anon;
256     } else {
257         realm = cell->realm;
258         strlcpy(localcell, realm, MAXCELLCHARS + 1);
259     }
260     if (realm)
261         if (realm == NULL) {
262             for (i = 0; (i < MAXCELLCHARS && cell->name[i]); i++) {
263                 if (isalpha(cell->name[i]))
264                     localcell[i] = toupper(cell->name[i]);
265                 else
266                     localcell[i] = cell->name[i];
267             }
268             localcell[i] = '\0';
269             realm = localcell;
270         }
271     cc = NULL;
272     code = krb5_cc_default(context, &cc);
273
274     memset(&match, 0, sizeof(match));
275     Z_enctype(Z_credskey(&match)) = ENCTYPE_DES_CBC_CRC;
276
277     if (code == 0)
278         code = krb5_cc_get_principal(context, cc, &match.client);
279     if (code == 0)
280         code = krb5_build_principal(context, &match.server,
281                                     strlen(realm), realm,
282                                     "afs", cell->name, NULL);
283
284     if (code != 0) {
285         krb5_free_cred_contents(context, &match);
286         if (cc)
287             krb5_cc_close(context, cc);
288         krb5_free_context(context);
289         goto try_anon;
290     }
291
292     code = krb5_get_credentials(context, 0, cc, &match, &cred);
293     if (code != 0) {
294         krb5_free_principal(context, match.server);
295         match.server = NULL;
296
297         code = krb5_build_principal(context, &match.server,
298                                     strlen(realm), realm, "afs", (void *)NULL);
299         if (code == 0)
300             code = krb5_get_credentials(context, 0, cc, &match, &cred);
301         if (code != 0) {
302             krb5_free_cred_contents(context, &match);
303             if (cc)
304                 krb5_cc_close(context, cc);
305             krb5_free_context(context);
306             goto try_anon;
307         }
308     }
309
310     if (insecure)
311         l = rxkad_clear;
312     else
313         l = rxkad_crypt;
314     memcpy(&k.data, Z_keydata(Z_credskey(cred)), 8);
315     sc = (struct rx_securityClass *)rxkad_NewClientSecurityObject
316         (l, &k, RXKAD_TKT_TYPE_KERBEROS_V5,
317          cred->ticket.length, cred->ticket.data);
318     krb5_free_creds(context, cred);
319     krb5_free_cred_contents(context, &match);
320     if (cc)
321         krb5_cc_close(context, cc);
322     krb5_free_context(context);
323     cell->security = sc;
324     cell->scindex = 2;
325     return 0;
326
327     try_anon:
328 #endif /* HAVE_KERBEROS */
329     if (try_anonymous)
330         return _GetNullSecurityObject(cell);
331     else
332         return code;
333 }
334
335 int
336 _GetVLservers(struct afscp_cell *cell)
337 {
338     struct rx_connection *conns[MAXHOSTSPERCELL + 1];
339     int i;
340     int code;
341     struct afsconf_cell celldata;
342
343     code = _GetCellInfo(cell->name, &celldata);
344     if (code != 0) {
345         return code;
346     }
347
348     for (i = 0; i < celldata.numServers; i++) {
349         conns[i] = rx_NewConnection(celldata.hostAddr[i].sin_addr.s_addr,
350                                     htons(AFSCONF_VLDBPORT),
351                                     USER_SERVICE_ID, cell->security,
352                                     cell->scindex);
353     }
354     conns[i] = 0;
355     return ubik_ClientInit(conns, &cell->vlservers);
356 }