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