af9623817cd99daa67edb18b064f7fe7c1b778e6
[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 HAVE_KRB5_GET_ERROR_MESSAGE
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 #ifdef HAVE_KRB5_FREE_ERROR_MESSAGE
73                 krb5_free_error_message(context, msg);
74 #elif defined(HAVE_KRB5_FREE_ERROR_STRING)
75                 {
76                         char *free;
77
78                         memcpy(&free, &msg, sizeof(free));
79                         krb5_free_error_string(context, free);
80                 }
81 #else
82 #  error "No way to free error strings, missing krb5_free_error_message() and krb5_free_error_string()"
83 #endif
84         } else {
85                 strlcpy(buffer, "Unknown error", KRB5_STRERROR_BUFSIZE);
86         }
87
88         return buffer;
89 }
90 #endif
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 _mod_conn_free(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
106 #ifdef HEIMDAL_KRB5
107         if (conn->ccache) {
108                 krb5_cc_destroy(conn->context, conn->ccache);
109         }
110 #endif
111
112         return 0;
113 }
114
115 /** Create and return a new connection
116  *
117  * libkrb5(s) can talk to the KDC over TCP. Were assuming something sane is implemented
118  * by libkrb5 and that it does connection caching associated with contexts, so it's
119  * worth using a connection pool to preserve connections when workers die.
120  *
121  * @param ctx to allocate connection handle memory in.
122  * @param instance rlm_krb5 instance instance.
123  * @return A new context or NULL on error.
124  */
125 void *mod_conn_create(TALLOC_CTX *ctx, void *instance)
126 {
127         rlm_krb5_t *inst = instance;
128         rlm_krb5_handle_t *conn;
129         krb5_error_code ret;
130
131         MEM(conn = talloc_zero(ctx, rlm_krb5_handle_t));
132         ret = krb5_init_context(&conn->context);
133         if (ret) {
134                 ERROR("rlm_krb5 (%s): Context initialisation failed: %s", inst->xlat_name,
135                        rlm_krb5_error(NULL, ret));
136
137                 return NULL;
138         }
139         talloc_set_destructor(conn, _mod_conn_free);
140
141         ret = inst->keytabname ?
142                 krb5_kt_resolve(conn->context, inst->keytabname, &conn->keytab) :
143                 krb5_kt_default(conn->context, &conn->keytab);
144         if (ret) {
145                 ERROR("Resolving keytab failed: %s", rlm_krb5_error(conn->context, ret));
146
147                 goto cleanup;
148         }
149
150 #ifdef HEIMDAL_KRB5
151         ret = krb5_cc_new_unique(conn->context, "MEMORY", NULL, &conn->ccache);
152         if (ret) {
153                 ERROR("rlm_krb5 (%s): Credential cache creation failed: %s", inst->xlat_name,
154                       rlm_krb5_error(conn->context, ret));
155
156                 return NULL;
157         }
158
159         krb5_verify_opt_init(&conn->options);
160         krb5_verify_opt_set_ccache(&conn->options, conn->ccache);
161
162         krb5_verify_opt_set_keytab(&conn->options, conn->keytab);
163         krb5_verify_opt_set_secure(&conn->options, true);
164
165         if (inst->service) {
166                 krb5_verify_opt_set_service(&conn->options, inst->service);
167         }
168 #else
169         krb5_verify_init_creds_opt_set_ap_req_nofail(inst->vic_options, true);
170 #endif
171         return conn;
172
173 cleanup:
174         talloc_free(conn);
175         return NULL;
176 }