Minor issues in heimdal kerberos
[freeradius.git] / src / modules / rlm_krb5 / krb5.c
1 /*
2  *   This program is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License as published by
4  *   the Free Software Foundation; either version 2 of the License, or
5  *   (at your option) any later version.
6  *
7  *   This program is distributed in the hope that it will be useful,
8  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *   GNU General Public License for more details.
11  *
12  *   You should have received a copy of the GNU General Public License
13  *   along with this program; if not, write to the Free Software
14  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16
17 /**
18  * $Id$
19  * @file krb5.h
20  * @brief Context management functions for rlm_krb5
21  *
22  * @copyright 2013  The FreeRADIUS server project
23  * @copyright 2013  Arran Cudbard-Bell <a.cudbardb@freeradius.org>
24  */
25 RCSID("$Id$")
26
27 #include <freeradius-devel/radiusd.h>
28 #include "krb5.h"
29
30 #ifdef HEIMDAL_KRB5
31 #  define KRB5_STRERROR_BUFSIZE (2048)
32
33 fr_thread_local_setup(char *, krb5_error_buffer)        /* macro */
34
35 /*
36  *      Explicitly cleanup the memory allocated to the error buffer.
37  */
38 static void _krb5_logging_free(void *arg)
39 {
40         free(arg);
41 }
42
43 char const *rlm_krb5_error(krb5_context context, krb5_error_code code)
44 {
45         char const *msg;
46         char *buffer;
47
48         buffer = fr_thread_local_init(krb5_error_buffer, _krb5_logging_free);
49         if (!buffer) {
50                 int ret;
51
52                 /*
53                  *      malloc is thread safe, talloc is not
54                  */
55                 buffer = malloc(sizeof(char) * KRB5_STRERROR_BUFSIZE);
56                 if (!buffer) {
57                         ERROR("Failed allocating memory for krb5 error buffer");
58                         return NULL;
59                 }
60
61                 ret = fr_thread_local_set(krb5_error_buffer, buffer);
62                 if (ret != 0) {
63                         ERROR("Failed setting up TLS for krb5 error buffer: %s", fr_syserror(ret));
64                         free(buffer);
65                         return NULL;
66                 }
67         }
68
69         msg = krb5_get_error_message(context, code);
70         if (msg) {
71                 strlcpy(buffer, msg, KRB5_STRERROR_BUFSIZE);
72                 krb5_free_error_string(context, msg);
73         } else {
74                 strlcpy(buffer, "Unknown error", KRB5_STRERROR_BUFSIZE);
75         }
76
77         return buffer;
78 }
79 #endif
80
81 /** Frees a krb5 context
82  *
83  * @param instance rlm_krb5 instance.
84  * @param handle to destroy.
85  * @return 0 (always indicates success).
86  */
87 int mod_conn_delete(UNUSED void *instance, void *handle)
88 {
89         return talloc_free((krb5_context *) handle);
90 }
91
92 /** Frees libkrb5 resources associated with the handle
93  *
94  * Must not be called directly.
95  *
96  * @param conn to free.
97  * @return 0 (always indicates success).
98  */
99 static int _free_handle(rlm_krb5_handle_t *conn) {
100         krb5_free_context(conn->context);
101
102         if (conn->keytab) {
103                 krb5_kt_close(conn->context, conn->keytab);
104         }
105         return 0;
106 }
107
108 /** Create and return a new connection
109  *
110  * libkrb5(s) can talk to the KDC over TCP. Were assuming something sane is implemented
111  * by libkrb5 and that it does connection caching associated with contexts, so it's
112  * worth using a connection pool to preserve connections when workers die.
113  *
114  * @param instance rlm_krb5 instance instance.
115  * @return A new context or NULL on error.
116  */
117 void *mod_conn_create(void *instance)
118 {
119         rlm_krb5_t *inst = instance;
120         rlm_krb5_handle_t *conn;
121         krb5_error_code ret;
122
123         MEM(conn = talloc_zero(instance, rlm_krb5_handle_t));
124         ret = krb5_init_context(&conn->context);
125         if (ret) {
126                 EDEBUG("rlm_krb5 (%s): Context initialisation failed: %s", inst->xlat_name,
127                        rlm_krb5_error(NULL, ret));
128
129                 return NULL;
130         }
131         talloc_set_destructor(conn, _free_handle);
132
133         ret = inst->keytabname ?
134                 krb5_kt_resolve(conn->context, inst->keytabname, &conn->keytab) :
135                 krb5_kt_default(conn->context, &conn->keytab);
136         if (ret) {
137                 ERROR("Resolving keytab failed: %s", rlm_krb5_error(conn->context, ret));
138
139                 goto cleanup;
140         }
141
142 #ifdef HEIMDAL_KRB5
143         /*
144          *      Setup krb5_verify_user options
145          *
146          *      Not entirely sure this is necessary, but as we use context
147          *      to get the cache handle, we probably do have to do this with
148          *      the cloned context.
149          */
150         krb5_cc_default(conn->context, &conn->ccache);
151
152         krb5_verify_opt_init(&conn->options);
153         krb5_verify_opt_set_ccache(&conn->options, conn->ccache);
154
155         krb5_verify_opt_set_keytab(&conn->options, conn->keytab);
156         krb5_verify_opt_set_secure(&conn->options, true);
157
158         if (inst->service) {
159                 krb5_verify_opt_set_service(&conn->options, inst->service);
160         }
161 #else
162         krb5_verify_init_creds_opt_set_ap_req_nofail(inst->vic_options, true);
163 #endif
164         return conn;
165
166 cleanup:
167         talloc_free(conn);
168         return NULL;
169 }