Merge branch 'master' into jennifer/trp-devel
[trust_router.git] / tid / tids.c
1 /*
2  * Copyright (c) 2012, 2015, 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 <assert.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <string.h>
40 #include <stdio.h>
41 #include <errno.h>
42 #include <sys/socket.h>
43 #include <sys/wait.h>
44 #include <netinet/in.h>
45 #include <jansson.h>
46 #include <talloc.h>
47 #include <tid_internal.h>
48 #include <gsscon.h>
49 #include <tr_debug.h>
50 #include <tr_msg.h>
51
52 static TID_RESP *tids_create_response (TIDS_INSTANCE *tids, TID_REQ *req) 
53 {
54   TID_RESP *resp=NULL;
55   int success=0;
56
57   if ((NULL == (resp = talloc_zero(req, TID_RESP)))) {
58     tr_crit("tids_create_response: Error allocating response structure.");
59     return NULL;
60   }
61   
62   resp->result = TID_SUCCESS; /* presume success */
63   if ((NULL == (resp->rp_realm = tr_dup_name(req->rp_realm))) ||
64       (NULL == (resp->realm = tr_dup_name(req->realm))) ||
65       (NULL == (resp->comm = tr_dup_name(req->comm)))) {
66     tr_crit("tids_create_response: Error allocating fields in response.");
67     goto cleanup;
68   }
69   if (req->orig_coi) {
70     if (NULL == (resp->orig_coi = tr_dup_name(req->orig_coi))) {
71       tr_crit("tids_create_response: Error allocating fields in response.");
72       goto cleanup;
73     }
74   }
75
76   success=1;
77
78 cleanup:
79   if ((!success) && (resp!=NULL)) {
80     if (resp->rp_realm!=NULL)
81       tr_free_name(resp->rp_realm);
82     if (resp->realm!=NULL)
83       tr_free_name(resp->realm);
84     if (resp->comm!=NULL)
85       tr_free_name(resp->comm);
86     if (resp->orig_coi!=NULL)
87       tr_free_name(resp->orig_coi);
88     talloc_free(resp);
89     resp=NULL;
90   }
91   return resp;
92 }
93
94 static void tids_destroy_response(TIDS_INSTANCE *tids, TID_RESP *resp) 
95 {
96   if (resp) {
97     if (resp->err_msg)
98       tr_free_name(resp->err_msg);
99     if (resp->rp_realm)
100       tr_free_name(resp->rp_realm);
101     if (resp->realm)
102       tr_free_name(resp->realm);
103     if (resp->comm)
104       tr_free_name(resp->comm);
105     if (resp->orig_coi)
106       tr_free_name(resp->orig_coi);
107     talloc_free(resp);
108   }
109 }
110
111 static int tids_listen (TIDS_INSTANCE *tids, int port) 
112 {
113     int rc = 0;
114     int conn = -1;
115     int optval = 1;
116
117     union {
118       struct sockaddr_storage storage;
119       struct sockaddr_in in4;
120     } addr;
121
122     struct sockaddr_in *saddr = (struct sockaddr_in *) &addr.in4;
123
124     saddr->sin_port = htons (port);
125     saddr->sin_family = AF_INET;
126     saddr->sin_addr.s_addr = INADDR_ANY;
127
128     if (0 > (conn = socket (AF_INET, SOCK_STREAM, 0)))
129       return conn;
130
131     setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
132
133     if (0 > (rc = bind (conn, (struct sockaddr *) saddr, sizeof(struct sockaddr_in))))
134       return rc;
135
136     if (0 > (rc = listen(conn, 512)))
137       return rc;
138
139     tr_debug("tids_listen: TID Server listening on port %d", port);
140     return conn;
141 }
142
143 /* returns EACCES if authorization is denied */
144 static int tids_auth_cb(gss_name_t clientName, gss_buffer_t displayName,
145                         void *data)
146 {
147   struct tids_instance *inst = (struct tids_instance *) data;
148   TR_NAME name ={(char *) displayName->value,
149                  displayName->length};
150   int result=0;
151
152   if (0!=inst->auth_handler(clientName, &name, inst->cookie)) {
153     tr_debug("tids_auth_cb: client '%.*s' denied authorization.", name.len, name.buf);
154     result=EACCES; /* denied */
155   }
156
157   return result;
158 }
159
160 /* returns 0 on authorization success, 1 on failure, or -1 in case of error */
161 static int tids_auth_connection (TIDS_INSTANCE *inst,
162                                  int conn,
163                                  gss_ctx_id_t *gssctx)
164 {
165   int rc = 0;
166   int auth, autherr = 0;
167   gss_buffer_desc nameBuffer = {0, NULL};
168   char *name = 0;
169   int nameLen = 0;
170
171   nameLen = asprintf(&name, "trustidentity@%s", inst->hostname);
172   nameBuffer.length = nameLen;
173   nameBuffer.value = name;
174
175   if (rc = gsscon_passive_authenticate(conn, nameBuffer, gssctx, tids_auth_cb, inst)) {
176     tr_debug("tids_auth_connection: Error from gsscon_passive_authenticate(), rc = %d.", rc);
177     return -1;
178   }
179
180   if (rc = gsscon_authorize(*gssctx, &auth, &autherr)) {
181     tr_debug("tids_auth_connection: Error from gsscon_authorize, rc = %d, autherr = %d.", 
182             rc, autherr);
183     return -1;
184   }
185
186   if (auth)
187     tr_debug("tids_auth_connection: Connection authenticated, conn = %d.", conn);
188   else
189     tr_debug("tids_auth_connection: Authentication failed, conn %d.", conn);
190
191   return !auth;
192 }
193
194 static int tids_read_request (TIDS_INSTANCE *tids, int conn, gss_ctx_id_t *gssctx, TR_MSG **mreq)
195 {
196   int err;
197   char *buf;
198   size_t buflen = 0;
199
200   if (err = gsscon_read_encrypted_token(conn, *gssctx, &buf, &buflen)) {
201     if (buf)
202       free(buf);
203     return -1;
204   }
205
206   tr_debug("tids_read_request():Request Received, %u bytes.", (unsigned) buflen);
207
208   /* Parse request */
209   if (NULL == ((*mreq) = tr_msg_decode(buf, buflen))) {
210     tr_debug("tids_read_request():Error decoding request.");
211     free (buf);
212     return -1;
213   }
214
215   /* If this isn't a TID Request, just drop it. */
216   if (TID_REQUEST != (*mreq)->msg_type) {
217     tr_debug("tids_read_request(): Not a TID Request, dropped.");
218     return -1;
219   }
220
221   free (buf);
222   return buflen;
223 }
224
225 static int tids_handle_request (TIDS_INSTANCE *tids, TR_MSG *mreq, TID_RESP *resp) 
226 {
227   int rc=-1;
228
229   /* Check that this is a valid TID Request.  If not, send an error return. */
230   if ((!tr_msg_get_req(mreq)) ||
231       (!tr_msg_get_req(mreq)->rp_realm) ||
232       (!tr_msg_get_req(mreq)->realm) ||
233       (!tr_msg_get_req(mreq)->comm)) {
234     tr_notice("tids_handle_request(): Not a valid TID Request.");
235     resp->result = TID_ERROR;
236     resp->err_msg = tr_new_name("Bad request format");
237     return -1;
238   }
239
240   tr_debug("tids_handle_request: adding self to req path.");
241   tid_req_add_path(tr_msg_get_req(mreq), tids->hostname, tids->tids_port);
242   
243   /* Call the caller's request handler */
244   /* TBD -- Handle different error returns/msgs */
245   if (0 > (rc = (*tids->req_handler)(tids, tr_msg_get_req(mreq), resp, tids->cookie))) {
246     /* set-up an error response */
247     tr_debug("tids_handle_request: req_handler returned error.");
248     resp->result = TID_ERROR;
249     if (!resp->err_msg) /* Use msg set by handler, if any */
250       resp->err_msg = tr_new_name("Internal processing error");
251   }
252   else {
253     /* set-up a success response */
254     tr_debug("tids_handle_request: req_handler returned success.");
255     resp->result = TID_SUCCESS;
256     resp->err_msg = NULL;       /* No error msg on successful return */
257   }
258     
259   return rc;
260 }
261
262 int tids_send_err_response (TIDS_INSTANCE *tids, TID_REQ *req, const char *err_msg) {
263   TID_RESP *resp = NULL;
264   int rc = 0;
265
266   /* If we already sent a response, don't send another no matter what. */
267   if (req->resp_sent)
268     return 0;
269
270   if (NULL == (resp = tids_create_response(tids, req))) {
271     tr_crit("tids_send_err_response: Can't create response.");
272     return -1;
273   }
274
275   
276   /* mark this as an error response, and include the error message */
277   resp->result = TID_ERROR;
278   resp->err_msg = tr_new_name((char *)err_msg);
279   resp->error_path = req->path;
280
281   rc = tids_send_response(tids, req, resp);
282   
283   tids_destroy_response(tids, resp);
284   return rc;
285 }
286
287 int tids_send_response (TIDS_INSTANCE *tids, TID_REQ *req, TID_RESP *resp)
288 {
289   int err;
290   TR_MSG mresp;
291   char *resp_buf;
292
293   if ((!tids) || (!req) || (!resp))
294     tr_debug("tids_send_response: Invalid parameters.");
295
296   /* Never send a second response if we already sent one. */
297   if (req->resp_sent)
298     return 0;
299
300   mresp.msg_type = TID_RESPONSE;
301   tr_msg_set_resp(&mresp, resp);
302
303   if (NULL == (resp_buf = tr_msg_encode(&mresp))) {
304
305     fprintf(stderr, "tids_send_response: Error encoding json response.\n");
306     tr_audit_req(req);
307
308     return -1;
309   }
310
311   tr_debug("tids_send_response: Encoded response: %s", resp_buf);
312
313   /* If external logging is enabled, fire off a message */
314   /* TODO Can be moved to end once segfault in gsscon_write_encrypted_token fixed */
315   tr_audit_resp(resp);
316
317   /* Send the response over the connection */
318   if (err = gsscon_write_encrypted_token (req->conn, req->gssctx, resp_buf, 
319                                           strlen(resp_buf) + 1)) {
320     tr_notice("tids_send_response: Error sending response over connection.");
321
322     tr_audit_req(req);
323
324     return -1;
325   }
326
327   /* indicate that a response has been sent for this request */
328   req->resp_sent = 1;
329
330   free(resp_buf);
331
332   return 0;
333 }
334
335 static void tids_handle_connection (TIDS_INSTANCE *tids, int conn)
336 {
337   TR_MSG *mreq = NULL;
338   TID_RESP *resp = NULL;
339   int rc = 0;
340   gss_ctx_id_t gssctx = GSS_C_NO_CONTEXT;
341
342   if (tids_auth_connection(tids, conn, &gssctx)) {
343     tr_notice("tids_handle_connection: Error authorizing TID Server connection.");
344     close(conn);
345     return;
346   }
347
348   tr_debug("tids_handle_connection: Connection authorized!");
349
350   while (1) {   /* continue until an error breaks us out */
351
352     if (0 > (rc = tids_read_request(tids, conn, &gssctx, &mreq))) {
353       tr_debug("tids_handle_connection: Error from tids_read_request(), rc = %d.", rc);
354       return;
355     } else if (0 == rc) {
356       continue;
357     }
358
359     /* Put connection information into the request structure */
360     tr_msg_get_req(mreq)->conn = conn;
361     tr_msg_get_req(mreq)->gssctx = gssctx;
362
363     /* Allocate a response structure and populate common fields */
364     if (NULL == (resp = tids_create_response (tids, tr_msg_get_req(mreq)))) {
365       tr_crit("tids_handle_connection: Error creating response structure.");
366       /* try to send an error */
367       tids_send_err_response(tids, tr_msg_get_req(mreq), "Error creating response.");
368       tr_msg_free_decoded(mreq);
369       return;
370     }
371
372     if (0 > (rc = tids_handle_request(tids, mreq, resp))) {
373       tr_debug("tids_handle_connection: Error from tids_handle_request(), rc = %d.", rc);
374       /* Fall through, to send the response, either way */
375     }
376
377     if (0 > (rc = tids_send_response(tids, tr_msg_get_req(mreq), resp))) {
378       tr_debug("tids_handle_connection: Error from tids_send_response(), rc = %d.", rc);
379       /* if we didn't already send a response, try to send a generic error. */
380       if (!tr_msg_get_req(mreq)->resp_sent)
381         tids_send_err_response(tids, tr_msg_get_req(mreq), "Error sending response.");
382       /* Fall through to free the response, either way. */
383     }
384     
385     tids_destroy_response(tids, resp);
386     tr_msg_free_decoded(mreq);
387     return;
388   } 
389 }
390
391 TIDS_INSTANCE *tids_create (TALLOC_CTX *mem_ctx)
392 {
393   return talloc_zero(mem_ctx, TIDS_INSTANCE);
394 }
395
396 /* Get a listener for tids requests, returns its socket fd. Accept
397  * connections with tids_accept() */
398 int tids_get_listener(TIDS_INSTANCE *tids, 
399                       TIDS_REQ_FUNC *req_handler,
400                       TIDS_AUTH_FUNC *auth_handler,
401                       const char *hostname,
402                       unsigned int port,
403                       void *cookie)
404 {
405   int listen = -1;
406
407   tids->tids_port = port;
408   if (0 > (listen = tids_listen(tids, port))) {
409     char errbuf[256];
410     if (0 == strerror_r(errno, errbuf, 256)) {
411       tr_debug("tids_get_listener: Error opening port %d: %s.", port, errbuf);
412     } else {
413       tr_debug("tids_get_listener: Unknown error openining port %d.", port);
414     }
415   } 
416
417   if (listen > 0) {
418     /* opening port succeeded */
419     tr_debug("tids_get_listener: Opened port %d.", port);
420     
421     /* make this socket non-blocking */
422     if (0 != fcntl(listen, F_SETFL, O_NONBLOCK)) {
423       tr_debug("tids_get_listener: Error setting O_NONBLOCK.");
424       close(listen);
425       listen=-1;
426     }
427   }
428
429   if (listen > 0) {
430     /* store the caller's request handler & cookie */
431     tids->req_handler = req_handler;
432     tids->auth_handler = auth_handler;
433     tids->hostname = hostname;
434     tids->cookie = cookie;
435   }
436
437   return listen;
438 }
439
440 /* Accept and process a connection on a port opened with tids_get_listener() */
441 int tids_accept(TIDS_INSTANCE *tids, int listen)
442 {
443   int conn=-1;
444   int pid=-1;
445
446   if (0 > (conn = accept(listen, NULL, NULL))) {
447     perror("Error from TIDS Server accept()");
448     return 1;
449   }
450
451   if (0 > (pid = fork())) {
452     perror("Error on fork()");
453     return 1;
454   }
455
456   if (pid == 0) {
457     close(listen);
458     tids_handle_connection(tids, conn);
459     close(conn);
460     exit(0); /* exit to kill forked child process */
461   } else {
462     close(conn);
463   }
464
465   /* clean up any processes that have completed  (TBD: move to main loop?) */
466   while (waitpid(-1, 0, WNOHANG) > 0);
467
468   return 0;
469 }
470
471 /* Process tids requests forever. Should not return except on error. */
472 int tids_start (TIDS_INSTANCE *tids, 
473                 TIDS_REQ_FUNC *req_handler,
474                 TIDS_AUTH_FUNC *auth_handler,
475                 const char *hostname,
476                 unsigned int port,
477                 void *cookie)
478 {
479   int listen = -1;
480   int conn = -1;
481   pid_t pid;
482
483   tids->tids_port = port;
484   if (0 > (listen = tids_listen(tids, port)))
485     perror ("Error from tids_listen()");
486
487   /* store the caller's request handler & cookie */
488   tids->req_handler = req_handler;
489   tids->auth_handler = auth_handler;
490   tids->hostname = hostname;
491   tids->cookie = cookie;
492
493   tr_info("Trust Path Query Server starting on host %s:%d.", hostname, port);
494
495   while(1) {    /* accept incoming conns until we are stopped */
496
497     if (0 > (conn = accept(listen, NULL, NULL))) {
498       perror("Error from TIDS Server accept()");
499       return 1;
500     }
501
502     if (0 > (pid = fork())) {
503       perror("Error on fork()");
504       return 1;
505     }
506
507     if (pid == 0) {
508       close(listen);
509       tids_handle_connection(tids, conn);
510       close(conn);
511       exit(0); /* exit to kill forked child process */
512     } else {
513       close(conn);
514     }
515
516     /* clean up any processes that have completed */
517     while (waitpid(-1, 0, WNOHANG) > 0);
518   }
519
520   return 1;     /* should never get here, loops "forever" */
521 }
522
523 void tids_destroy (TIDS_INSTANCE *tids)
524 {
525   /* clean up logfiles */
526   tr_log_close();
527
528   if (tids)
529     free(tids);
530 }