2 * Copyright (c) 2018, JANET(UK)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of JANET(UK) nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31 * OF THE POSSIBILITY OF SUCH DAMAGE.
45 * tr_gss.c - GSS connection handler
47 * The chief entry point to this module is tr_gss_handle_connection(). This
48 * function accepts an incoming socket connection, runs the GSS authorization
49 * and authentication process, accepts a request, processes it, then sends
50 * the reply and returns without closing the connection.
52 * Callers need to provide two callbacks, each with a cookie for passing
53 * custom data to the callback.
55 * * TR_GSS_AUTH_FN auth_cb: Authorization callback
56 * - This callback is used during the GSS auth process to determine whether
57 * a credential should be authorized to connect.
59 * * TR_GSS_HANDLE_REQ_FN req_cb: Request handler callback
60 * - After auth, this callback is passed the string form of the incoming request.
61 * It should process the request and return a string form of the outgoing
65 typedef struct tr_gss_cookie {
66 TR_GSS_AUTH_FN *auth_cb;
70 static int tr_gss_auth_cb(gss_name_t clientName, gss_buffer_t displayName, void *data)
72 TR_GSS_COOKIE *cookie = talloc_get_type_abort(data, TR_GSS_COOKIE);
73 TR_NAME name ={(char *) displayName->value, (int) displayName->length};
76 if (cookie->auth_cb(clientName, &name, cookie->auth_cookie)) {
77 tr_debug("tr_gss_auth_cb: client '%.*s' denied authorization.", name.len, name.buf);
78 result=EACCES; /* denied */
86 * Handle GSS authentication and authorization
88 * @param conn connection file descriptor
89 * @param acceptor_name name of acceptor to present to initiator
90 * @param acceptor_realm realm of acceptor to present to initiator
91 * @param gssctx GSS context
92 * @param auth_cb authorization callback
93 * @param auth_cookie generic data to pass to the authorization callback
94 * @return 0 on successful auth, 1 on disallowed auth, -1 on error
96 static int tr_gss_auth_connection(int conn,
97 const char *acceptor_name,
98 const char *acceptor_realm,
100 TR_GSS_AUTH_FN auth_cb,
104 int auth, autherr = 0;
105 gss_buffer_desc nameBuffer = {0, NULL};
106 TR_GSS_COOKIE *cookie = NULL;
108 nameBuffer.value = talloc_asprintf(NULL, "%s@%s", acceptor_name, acceptor_realm);
109 if (nameBuffer.value == NULL) {
110 tr_err("tr_gss_auth_connection: Error allocating acceptor name.");
113 nameBuffer.length = strlen(nameBuffer.value);
115 /* Set up for the auth callback. There are two layers of callbacks here: we
116 * use our own, which handles gsscon interfacing and calls the auth_cb parameter
117 * to do the actual auth. Store the auth_cb information in a metacookie. */
118 cookie = talloc(NULL, TR_GSS_COOKIE);
119 cookie->auth_cb=auth_cb;
120 cookie->auth_cookie=auth_cookie;
122 /* Now call gsscon with *our* auth callback and cookie */
123 rc = gsscon_passive_authenticate(conn, nameBuffer, gssctx, tr_gss_auth_cb, cookie);
125 talloc_free(nameBuffer.value);
127 tr_debug("tr_gss_auth_connection: Error from gsscon_passive_authenticate(), rc = %d.", rc);
131 rc = gsscon_authorize(*gssctx, &auth, &autherr);
133 tr_debug("tr_gss_auth_connection: Error from gsscon_authorize, rc = %d, autherr = %d.",
139 tr_debug("tr_gss_auth_connection: Connection authenticated, conn = %d.", conn);
141 tr_debug("tr_gss_auth_connection: Authentication failed, conn %d.", conn);
147 * Read a request from the GSS connection
149 * @param mem_ctx talloc context for the result
150 * @param conn file descriptor for the connection
151 * @param gssctx GSS context
152 * @return talloc'ed string containing the request, or null on error
154 static char *tr_gss_read_req(TALLOC_CTX *mem_ctx, int conn, gss_ctx_id_t gssctx)
161 err = gsscon_read_encrypted_token(conn, gssctx, &buf, &buflen);
162 if (err || (buf == NULL)) {
165 tr_debug("tr_gss_read_req: Error reading from connection, rc=%d", err);
169 tr_debug("tr_gss_read_req: Read %u bytes.", (unsigned) buflen);
171 // get a talloc'ed version, guaranteed to have a null termination
172 retval = talloc_asprintf(mem_ctx, "%.*s", (int) buflen, buf);
179 * Write a response to the GSS connection
181 * @param conn file descriptor for the connection
182 * @param gssctx GSS context
183 * @param resp encoded response string to send
184 * @return 0 on success, -1 on error
186 static int tr_gss_write_resp(int conn, gss_ctx_id_t gssctx, const char *resp)
190 /* Send the response over the connection */
191 err = gsscon_write_encrypted_token (conn, gssctx, resp, strlen(resp) + 1);
193 tr_debug("tr_gss_send_response: Error sending response over connection, rc=%d.", err);
200 * Handle a request/response connection
202 * Authorizes/authenticates the connection, then reads a response, passes that to a
203 * callback to get a response, sends that, then returns.
205 * @param conn connection file descriptor
206 * @param acceptor_name acceptor name to present
207 * @param acceptor_realm acceptor realm to present
208 * @param auth_cb callback for authorization
209 * @param auth_cookie cookie for the auth_cb
210 * @param req_cb callback to handle the request and produce the response
211 * @param req_cookie cookie for the req_cb
213 void tr_gss_handle_connection(int conn,
214 const char *acceptor_name,
215 const char *acceptor_realm,
216 TR_GSS_AUTH_FN auth_cb,
218 TR_GSS_HANDLE_REQ_FN req_cb,
221 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
222 gss_ctx_id_t gssctx = GSS_C_NO_CONTEXT;
223 char *req_str = NULL;
225 TR_MSG *req_msg = NULL;
226 TR_MSG *resp_msg = NULL;
227 char *resp_str = NULL;
229 if (tr_gss_auth_connection(conn,
235 tr_notice("tr_gss_handle_connection: Error authorizing connection.");
239 tr_debug("tr_gss_handle_connection: Connection authorized");
241 // TODO: should there be a timeout on this?
243 /* continue until an error breaks us out */
244 // try to read a request
245 req_str = tr_gss_read_req(tmp_ctx, conn, gssctx);
247 if (req_str == NULL) {
248 // an error occurred, give up
249 tr_notice("tr_gss_handle_connection: Error reading request");
253 req_len = strlen(req_str);
255 /* If we got no characters, we will loop again. Free the empty response for the next loop. */
257 talloc_free(req_str);
259 } while (req_len == 0);
261 /* Decode the request */
262 req_msg = tr_msg_decode(tmp_ctx, req_str, req_len);
263 if (req_msg == NULL) {
264 tr_notice("tr_gss_handle_connection: Error decoding response");
268 /* Hand off the request for processing and get the response */
269 resp_msg = req_cb(tmp_ctx, req_msg, req_cookie);
271 if (resp_msg == NULL) {
272 // no response, clean up
276 /* Encode the response */
277 resp_str = tr_msg_encode(tmp_ctx, resp_msg);
278 if (resp_str == NULL) {
279 /* We apparently can't encode a response, so just return */
280 tr_err("tr_gss_handle_connection: Error encoding response");
285 if (tr_gss_write_resp(conn, gssctx, resp_str)) {
286 tr_err("tr_gss_handle_connection: Error writing response");
291 talloc_free(tmp_ctx);