495f8f860cee42140a8ebf9877d80677f00d17b6
[trust_router.git] / common / tr_gss.c
1 /*
2  * Copyright (c) 2018, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
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.
15  *
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.
19  *
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.
32  *
33  */
34
35 #include <talloc.h>
36 #include <gssapi.h>
37 #include <string.h>
38
39 #include <tr_msg.h>
40 #include <tr_debug.h>
41 #include <gsscon.h>
42 #include <tr_gss.h>
43
44
45 /**
46  * Callback to handle GSS authentication and authorization
47  *
48  * @param conn connection file descriptor
49  * @param acceptor_name name of acceptor to present to initiator
50  * @param acceptor_realm realm of acceptor to present to initiator
51  * @param gssctx GSS context
52  * @param auth_cb authorization callback
53  * @param auth_cookie generic data to pass to the authorization callback
54  * @return 0 on successful auth, 1 on disallowed auth, -1 on error
55  */
56 static int tr_gss_auth_connection(int conn,
57                                   const char *acceptor_name,
58                                   const char *acceptor_realm,
59                                   gss_ctx_id_t *gssctx,
60                                   TR_GSS_AUTH_FN auth_cb,
61                                   void *auth_cookie)
62 {
63   int rc = 0;
64   int auth, autherr = 0;
65   gss_buffer_desc nameBuffer = {0, NULL};
66
67   nameBuffer.value = talloc_asprintf(NULL, "%s@%s", acceptor_name, acceptor_realm);
68   if (nameBuffer.value == NULL) {
69     tr_err("tr_gss_auth_connection: Error allocating acceptor name.");
70     return -1;
71   }
72   nameBuffer.length = strlen(nameBuffer.value);
73
74   rc = gsscon_passive_authenticate(conn, nameBuffer, gssctx, auth_cb, auth_cookie);
75   talloc_free(nameBuffer.value);
76   if (rc) {
77     tr_debug("tr_gss_auth_connection: Error from gsscon_passive_authenticate(), rc = %d.", rc);
78     return -1;
79   }
80
81   rc = gsscon_authorize(*gssctx, &auth, &autherr);
82   if (rc) {
83     tr_debug("tr_gss_auth_connection: Error from gsscon_authorize, rc = %d, autherr = %d.",
84              rc, autherr);
85     return -1;
86   }
87
88   if (auth)
89     tr_debug("tr_gss_auth_connection: Connection authenticated, conn = %d.", conn);
90   else
91     tr_debug("tr_gss_auth_connection: Authentication failed, conn %d.", conn);
92
93   return !auth;
94 }
95
96 /**
97  * Read a request from the GSS connection
98  *
99  * @param mem_ctx talloc context for the result
100  * @param conn file descriptor for the connection
101  * @param gssctx GSS context
102  * @return talloc'ed string containing the request, or null on error
103  */
104 static char *tr_gss_read_req(TALLOC_CTX *mem_ctx, int conn, gss_ctx_id_t gssctx)
105 {
106   int err;
107   char *retval = NULL;
108   char *buf = NULL;
109   size_t buflen = 0;
110
111   err = gsscon_read_encrypted_token(conn, gssctx, &buf, &buflen);
112   if (err || (buf == NULL)) {
113     if (buf)
114       free(buf);
115     tr_debug("tr_gss_read_req: Error reading from connection, rc=%d", err);
116     return NULL;
117   }
118
119   tr_debug("tr_gss_read_req: Read %u bytes.", (unsigned) buflen);
120
121   // get a talloc'ed version, guaranteed to have a null termination
122   retval = talloc_asprintf(mem_ctx, "%.*s", (int) buflen, buf);
123   free(buf);
124
125   return retval;
126 }
127
128 /**
129  * Write a response to the GSS connection
130  *
131  * @param conn file descriptor for the connection
132  * @param gssctx GSS context
133  * @param resp encoded response string to send
134  * @return 0 on success, -1 on error
135  */
136 static int tr_gss_write_resp(int conn, gss_ctx_id_t gssctx, const char *resp)
137 {
138   int err = 0;
139
140   /* Send the response over the connection */
141   err = gsscon_write_encrypted_token (conn, gssctx, resp, strlen(resp) + 1);
142   if (err) {
143     tr_debug("tr_gss_send_response: Error sending response over connection, rc=%d.", err);
144     return -1;
145   }
146   return 0;
147 }
148
149 /**
150  * Handle a request/response connection
151  *
152  * Authorizes/authenticates the connection, then reads a response, passes that to a
153  * callback to get a response, sends that, then returns.
154  *
155  * @param conn connection file descriptor
156  * @param acceptor_name acceptor name to present
157  * @param acceptor_realm acceptor realm to present
158  * @param auth_cb callback for authorization
159  * @param auth_cookie cookie for the auth_cb
160  * @param req_cb callback to handle the request and produce the response
161  * @param req_cookie cookie for the req_cb
162  */
163 void tr_gss_handle_connection(int conn,
164                               const char *acceptor_name,
165                               const char *acceptor_realm,
166                               TR_GSS_AUTH_FN auth_cb,
167                               void *auth_cookie,
168                               TR_GSS_HANDLE_REQ_FN req_cb,
169                               void *req_cookie)
170 {
171   TALLOC_CTX *tmp_ctx = talloc_new(NULL);
172   gss_ctx_id_t gssctx = GSS_C_NO_CONTEXT;
173   char *req_str = NULL;
174   char *resp_str = NULL;
175
176   if (tr_gss_auth_connection(conn,
177                              acceptor_name,
178                              acceptor_realm,
179                              &gssctx,
180                              auth_cb,
181                              auth_cookie)) {
182     tr_notice("tr_gss_handle_connection: Error authorizing connection.");
183     goto cleanup;
184   }
185
186   tr_debug("tr_gss_handle_connection: Connection authorized");
187
188   // TODO: should there be a timeout on this?
189   while (1) {   /* continue until an error breaks us out */
190     // try to read a request
191     req_str = tr_gss_read_req(tmp_ctx, conn, gssctx);
192
193     if ( req_str == NULL) {
194       // an error occurred, give up
195       tr_notice("tr_gss_handle_connection: Error reading request");
196       goto cleanup;
197     } else if (strlen(req_str) > 0) {
198       // we got a request message, exit the loop and process it
199       break;
200     }
201
202     // no error, but no message, keep waiting for one
203     talloc_free(req_str); // this would be cleaned up anyway, but may as well free it
204   }
205
206   /* Hand off the request for processing and get the response */
207   resp_str = req_cb(tmp_ctx, req_str, req_cookie);
208
209   if (resp_str == NULL) {
210     // no response, clean up
211     goto cleanup;
212   }
213
214   // send the response
215   if (tr_gss_write_resp(conn, gssctx, resp_str)) {
216     tr_notice("tr_gss_handle_connection: Error writing response");
217   }
218
219 cleanup:
220   talloc_free(tmp_ctx);
221 }