Avoid calling krb5_free_context(NULL)
[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 # define KERBEROS_APPLE_DEPRECATED(x)
45 # include <krb5.h>
46 #endif
47 #include "afscp.h"
48 #include "afscp_internal.h"
49
50 #define HC_DEPRECATED
51 #include <hcrypto/des.h>
52
53 #ifdef HAVE_KRB5_CREDS_KEYBLOCK_ENCTYPE
54 #define Z_keydata(keyblock)     ((keyblock)->contents)
55 #define Z_keylen(keyblock)      ((keyblock)->length)
56 #define Z_credskey(creds)       (&(creds)->keyblock)
57 #define Z_enctype(keyblock)     ((keyblock)->enctype)
58 #else
59 #define Z_keydata(keyblock)     ((keyblock)->keyvalue.data)
60 #define Z_keylen(keyblock)      ((keyblock)->keyvalue.length)
61 #define Z_credskey(creds)       (&(creds)->session)
62 #define Z_enctype(keyblock)     ((keyblock)->keytype)
63 #endif
64
65 static int insecure = 0;
66 static int try_anonymous = 0;
67 static char authas_name[256];
68 static char authas_inst[256];
69
70 int
71 afscp_Insecure(void)
72 {
73     insecure = 1;
74     return 0;
75 }
76
77 int
78 afscp_AnonymousAuth(int state)
79 {
80     try_anonymous = state;
81     return 0;
82 }
83
84 /**
85  * Connect to all servers using authenticated connections, using the local
86  * KeyFile to appear as an arbitrary user.
87  *
88  * @param[in] aname  The pts username to impersonate
89  *
90  * @note aname is krb4-based name, not a krb5 principal. So for example, you
91  *       probably want to give "user.admin" instead of "user/admin".
92  *
93  * @return operation status
94  *  @retval 0 success
95  */
96 int
97 afscp_LocalAuthAs(const char *aname)
98 {
99     const char *ainst = strchr(aname, '.');
100     size_t namelen, instlen;
101
102     if (ainst) {
103         namelen = ainst - aname;
104         ainst++;
105         instlen = strlen(ainst);
106     } else {
107         namelen = strlen(aname);
108         ainst = "";
109         instlen = 0;
110     }
111
112     if (namelen+1 > sizeof(authas_name) || instlen+1 > sizeof(authas_inst)) {
113         return EINVAL;
114     }
115     strncpy(authas_name, aname, namelen);
116     strncpy(authas_inst, ainst, instlen);
117     return 0;
118 }
119
120 static struct afsconf_dir *confdir;
121
122 void
123 afscp_SetConfDir(char *confDir)
124 {
125     if (confdir != NULL)
126         afsconf_Close(confdir);
127
128     confdir = afsconf_Open(confDir);
129 }
130
131 static int
132 _GetCellInfo(char *cell, struct afsconf_cell *celldata)
133 {
134     int code;
135     if (confdir == NULL)
136         confdir = afsconf_Open(AFSCONF_CLIENTNAME);
137     if (confdir == NULL) {
138         return AFSCONF_NODB;
139     }
140     code = afsconf_GetCellInfo(confdir, cell, AFSCONF_VLDBSERVICE, celldata);
141     return code;
142 }
143
144 static int
145 _GetNullSecurityObject(struct afscp_cell *cell)
146 {
147     cell->security = (struct rx_securityClass *)rxnull_NewClientSecurityObject();
148     cell->scindex = RX_SECIDX_NULL;
149     return 0;
150 }
151
152 static int
153 _GetLocalSecurityObject(struct afscp_cell *cell,
154                         char *aname, char *ainst)
155 {
156     int code = 0;
157     char tbuffer[256];
158     struct ktc_encryptionKey key, session;
159     struct rx_securityClass *tc;
160     afs_int32 kvno;
161     afs_int32 ticketLen;
162     rxkad_level lev;
163     struct afsconf_dir *tdir;
164
165     tdir = afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH);
166     if (!tdir) {
167         code = AFSCONF_FAILURE;
168         goto done;
169     }
170
171     code = afsconf_GetLatestKey(tdir, &kvno, &key);
172     if (code) {
173         goto done;
174     }
175
176     DES_init_random_number_generator((DES_cblock *)&key);
177     code = DES_new_random_key((DES_cblock *)&session);
178     if (code) {
179         goto done;
180     }
181
182     ticketLen = sizeof(tbuffer);
183     memset(tbuffer, 0, sizeof(tbuffer));
184     code = tkt_MakeTicket(tbuffer, &ticketLen, &key, aname, ainst, "", 0,
185                           0xffffffff, &session, 0, "afs", "");
186     if (code) {
187         goto done;
188     }
189
190     if (insecure) {
191         lev = rxkad_clear;
192     } else {
193         lev = rxkad_crypt;
194     }
195
196     tc = (struct rx_securityClass *)
197         rxkad_NewClientSecurityObject(lev, &session, kvno, ticketLen,
198                                       tbuffer);
199     if (!tc) {
200         code = RXKADBADKEY;
201         goto done;
202     }
203
204     cell->security = tc;
205     cell->scindex = 2;
206
207  done:
208     if (tdir) {
209         afsconf_Close(tdir);
210     }
211     return code;
212 }
213
214 int
215 _GetSecurityObject(struct afscp_cell *cell)
216 {
217     int code = ENOENT;
218 #ifdef HAVE_KERBEROS
219     krb5_context context = NULL;
220     krb5_creds match;
221     krb5_creds *cred;
222     krb5_ccache cc;
223     char **realms, *realm;
224     struct afsconf_cell celldata;
225     char localcell[MAXCELLCHARS + 1];
226     struct rx_securityClass *sc;
227     struct ktc_encryptionKey k;
228     int i;
229     rxkad_level l;
230     code = _GetCellInfo(cell->name, &celldata);
231     if (code != 0) {
232         goto try_anon;
233     }
234
235     if (authas_name[0]) {
236         code = _GetLocalSecurityObject(cell, authas_name, authas_inst);
237         if (code == 0) {
238             return 0;
239         }
240     }
241
242     code = krb5_init_context(&context); /* see aklog.c main() */
243     if (code != 0) {
244         goto try_anon;
245     }
246
247     if (cell->realm == NULL) {
248         realm = NULL;
249         code = krb5_get_host_realm(context, celldata.hostName[0], &realms);
250
251         if (code == 0) {
252             strlcpy(localcell, realms[0], sizeof(localcell));
253             krb5_free_host_realm(context, realms);
254             realm = localcell;
255         } else
256             goto try_anon;
257     } else {
258         realm = cell->realm;
259         strlcpy(localcell, realm, MAXCELLCHARS + 1);
260     }
261     if (realm)
262         if (realm == NULL) {
263             for (i = 0; (i < MAXCELLCHARS && cell->name[i]); i++) {
264                 if (isalpha(cell->name[i]))
265                     localcell[i] = toupper(cell->name[i]);
266                 else
267                     localcell[i] = cell->name[i];
268             }
269             localcell[i] = '\0';
270             realm = localcell;
271         }
272     cc = NULL;
273     code = krb5_cc_default(context, &cc);
274
275     memset(&match, 0, sizeof(match));
276     Z_enctype(Z_credskey(&match)) = ENCTYPE_DES_CBC_CRC;
277
278     if (code == 0)
279         code = krb5_cc_get_principal(context, cc, &match.client);
280     if (code == 0)
281         code = krb5_build_principal(context, &match.server,
282                                     strlen(realm), realm,
283                                     "afs", cell->name, NULL);
284
285     if (code != 0) {
286         krb5_free_cred_contents(context, &match);
287         if (cc)
288             krb5_cc_close(context, cc);
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", 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             goto try_anon;
306         }
307     }
308
309     if (insecure)
310         l = rxkad_clear;
311     else
312         l = rxkad_crypt;
313     memcpy(&k.data, Z_keydata(Z_credskey(cred)), 8);
314     sc = (struct rx_securityClass *)rxkad_NewClientSecurityObject
315         (l, &k, RXKAD_TKT_TYPE_KERBEROS_V5,
316          cred->ticket.length, cred->ticket.data);
317     krb5_free_creds(context, cred);
318     krb5_free_cred_contents(context, &match);
319     if (cc)
320         krb5_cc_close(context, cc);
321     krb5_free_context(context);
322     cell->security = sc;
323     cell->scindex = 2;
324     return 0;
325
326  try_anon:
327     if (context != NULL) {
328         krb5_free_context(context);
329     }
330 #endif /* HAVE_KERBEROS */
331     if (try_anonymous)
332         return _GetNullSecurityObject(cell);
333     else
334         return code;
335 }
336
337 int
338 _GetVLservers(struct afscp_cell *cell)
339 {
340     struct rx_connection *conns[MAXHOSTSPERCELL + 1];
341     int i;
342     int code;
343     struct afsconf_cell celldata;
344
345     code = _GetCellInfo(cell->name, &celldata);
346     if (code != 0) {
347         return code;
348     }
349
350     for (i = 0; i < celldata.numServers; i++) {
351         conns[i] = rx_NewConnection(celldata.hostAddr[i].sin_addr.s_addr,
352                                     htons(AFSCONF_VLDBPORT),
353                                     USER_SERVICE_ID, cell->security,
354                                     cell->scindex);
355     }
356     conns[i] = 0;
357     return ubik_ClientInit(conns, &cell->vlservers);
358 }