Merge branch 'jennifer/march2016-patches'
[trust_router.git] / tr / tr_main.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 <stdio.h>
36 #include <stdlib.h>
37 #include <jansson.h>
38 #include <argp.h>
39
40 #include <tr.h>
41 #include <tr_filter.h>
42 #include <tid_internal.h>
43 #include <tr_config.h>
44 #include <tr_comm.h>
45 #include <tr_idp.h>
46 #include <tr_rp.h>
47 #include <tr_debug.h>
48
49 /* Structure to hold TR instance and original request in one cookie */
50 typedef struct tr_resp_cookie {
51   TR_INSTANCE *tr;
52   TID_REQ *orig_req;
53 } TR_RESP_COOKIE;
54
55
56 static void tr_tidc_resp_handler (TIDC_INSTANCE *tidc, 
57                         TID_REQ *req,
58                         TID_RESP *resp, 
59                         void *resp_cookie) 
60 {
61   tr_debug("tr_tidc_resp_handler: Response received (conn = %d)! Realm = %s, Community = %s.", ((TR_RESP_COOKIE *)resp_cookie)->orig_req->conn, resp->realm->buf, resp->comm->buf);
62   req->resp_rcvd = 1;
63
64   /* TBD -- handle concatentation of multiple responses to single req */
65   tids_send_response(((TR_RESP_COOKIE *)resp_cookie)->tr->tids, 
66                      ((TR_RESP_COOKIE *)resp_cookie)->orig_req, 
67                      resp);
68   
69   return;
70 }
71
72 static int tr_tids_req_handler (TIDS_INSTANCE *tids,
73                       TID_REQ *orig_req, 
74                       TID_RESP *resp,
75                       void *tr_in)
76 {
77   TIDC_INSTANCE *tidc = NULL;
78   TR_RESP_COOKIE resp_cookie;
79   TR_AAA_SERVER *aaa_servers = NULL;
80   TR_NAME *apc = NULL;
81   TID_REQ *fwd_req = NULL;
82   TR_COMM *cfg_comm = NULL;
83   TR_COMM *cfg_apc = NULL;
84   TR_INSTANCE *tr = (TR_INSTANCE *) tr_in;
85   int oaction = TR_FILTER_ACTION_REJECT;
86   int rc = 0;
87   time_t expiration_interval;
88
89   if ((!tids) || (!orig_req) || (!resp) ||  (!tr)) {
90     tr_debug("tr_tids_req_handler: Bad parameters");
91     return -1;
92   }
93
94   tr_debug("tr_tids_req_handler: Request received (conn = %d)! Realm = %s, Comm = %s", orig_req->conn, 
95          orig_req->realm->buf, orig_req->comm->buf);
96   if (tids)
97     tids->req_count++;
98
99   /* Duplicate the request, so we can modify and forward it */
100   if (NULL == (fwd_req = tid_dup_req(orig_req))) {
101     tr_debug("tr_tids_req_handler: Unable to duplicate request.");
102     return -1;
103   }
104
105   if (NULL == (cfg_comm = tr_comm_lookup(tids->cookie, orig_req->comm))) {
106     tr_notice("tr_tids_req_hander: Request for unknown comm: %s.", orig_req->comm->buf);
107     tids_send_err_response(tids, orig_req, "Unknown community");
108     return -1;
109   }
110
111   /* Check that the rp_realm matches the filter for the GSS name that 
112    * was received. */
113
114   if ((!(tr)->rp_gss) || 
115       (!(tr)->rp_gss->filter)) {
116     tr_notice("tr_tids_req_handler: No GSS name for incoming request.");
117     tids_send_err_response(tids, orig_req, "No GSS name for request");
118     return -1;
119   }
120
121   if ((TR_FILTER_NO_MATCH == tr_filter_process_rp_permitted(orig_req->rp_realm, (tr)->rp_gss->filter, orig_req->cons, &fwd_req->cons, &oaction)) ||
122       (TR_FILTER_ACTION_REJECT == oaction)) {
123     tr_notice("tr_tids_req_handler: RP realm (%s) does not match RP Realm filter for GSS name", orig_req->rp_realm->buf);
124     tids_send_err_response(tids, orig_req, "RP Realm filter error");
125     return -1;
126   }
127   /* Check that the rp_realm is a member of the community in the request */
128   if (NULL == (tr_find_comm_rp(cfg_comm, orig_req->rp_realm))) {
129     tr_notice("tr_tids_req_handler: RP Realm (%s) not member of community (%s).", orig_req->rp_realm->buf, orig_req->comm->buf);
130     tids_send_err_response(tids, orig_req, "RP COI membership error");
131     return -1;
132   }
133
134   /* Map the comm in the request from a COI to an APC, if needed */
135   if (TR_COMM_COI == cfg_comm->type) {
136     tr_debug("tr_tids_req_handler: Community was a COI, switching.");
137     /* TBD -- In theory there can be more than one?  How would that work? */
138     if ((!cfg_comm->apcs) || (!cfg_comm->apcs->id)) {
139       tr_notice("No valid APC for COI %s.", orig_req->comm->buf);
140       tids_send_err_response(tids, orig_req, "No valid APC for community");
141       return -1;
142     }
143     apc = tr_dup_name(cfg_comm->apcs->id);
144
145     /* Check that the APC is configured */
146     if (NULL == (cfg_apc = tr_comm_lookup(tids->cookie, apc))) {
147       tr_notice("tr_tids_req_hander: Request for unknown comm: %s.", apc->buf);
148       tids_send_err_response(tids, orig_req, "Unknown APC");
149       return -1;
150     }
151
152     fwd_req->comm = apc;
153     fwd_req->orig_coi = orig_req->comm;
154
155     /* Check that rp_realm is a  member of this APC */
156     if (NULL == (tr_find_comm_rp(cfg_apc, orig_req->rp_realm))) {
157       tr_notice("tr_tids_req_hander: RP Realm (%s) not member of community (%s).", orig_req->rp_realm->buf, orig_req->comm->buf);
158       tids_send_err_response(tids, orig_req, "RP APC membership error");
159       return -1;
160     }
161   }
162
163   /* Find the AAA server(s) for this request */
164   if (NULL == (aaa_servers = tr_idp_aaa_server_lookup((TR_INSTANCE *)tids->cookie, 
165                                                       orig_req->realm, 
166                                                       orig_req->comm))) {
167       tr_debug("tr_tids_req_handler: No AAA Servers for realm %s, defaulting.", orig_req->realm->buf);
168       if (NULL == (aaa_servers = tr_default_server_lookup ((TR_INSTANCE *)tids->cookie,
169                                                            orig_req->comm))) {
170         tr_notice("tr_tids_req_handler: No default AAA servers, discarded.");
171         tids_send_err_response(tids, orig_req, "No path to AAA Server(s) for realm");
172         return -1;
173       }
174   } else {
175     /* if we aren't defaulting, check idp coi and apc membership */
176     if (NULL == (tr_find_comm_idp(cfg_comm, fwd_req->realm))) {
177       tr_notice("tr_tids_req_handler: IDP Realm (%s) not member of community (%s).", orig_req->realm->buf, orig_req->comm->buf);
178       tids_send_err_response(tids, orig_req, "IDP community membership error");
179       return -1;
180     }
181     if ( cfg_apc && (NULL == (tr_find_comm_idp(cfg_apc, fwd_req->realm)))) {
182       tr_notice("tr_tids_req_handler: IDP Realm (%s) not member of APC (%s).", orig_req->realm->buf, orig_req->comm->buf);
183       tids_send_err_response(tids, orig_req, "IDP APC membership error");
184       return -1;
185     }
186   }
187
188   /* send a TID request to the AAA server(s), and get the answer(s) */
189   /* TBD -- Handle multiple servers */
190
191   if (cfg_apc)
192     expiration_interval = cfg_apc->expiration_interval;
193   else expiration_interval = cfg_comm->expiration_interval;
194   if (fwd_req->expiration_interval)
195     fwd_req->expiration_interval =  (expiration_interval < fwd_req->expiration_interval) ? expiration_interval : fwd_req->expiration_interval;
196   else fwd_req->expiration_interval = expiration_interval;
197   /* Create a TID client instance */
198   if (NULL == (tidc = tidc_create())) {
199     tr_crit("tr_tids_req_hander: Unable to allocate TIDC instance.");
200     tids_send_err_response(tids, orig_req, "Memory allocation failure");
201     return -1;
202   }
203   /* Use the DH parameters from the original request */
204   /* TBD -- this needs to be fixed when we handle more than one req per conn */
205   tidc->client_dh = orig_req->tidc_dh;
206
207   /* Save information about this request for the response */
208   resp_cookie.tr = tr;
209   resp_cookie.orig_req = orig_req;
210
211   /* Set-up TID connection */
212   if (-1 == (fwd_req->conn = tidc_open_connection(tidc, 
213                                                   aaa_servers->hostname->buf,
214                                                   TID_PORT,
215                                               &(fwd_req->gssctx)))) {
216     tr_notice("tr_tids_req_handler: Error in tidc_open_connection.");
217     tids_send_err_response(tids, orig_req, "Can't open connection to next hop TIDS");
218     return -1;
219   };
220
221   /* Send a TID request */
222   if (0 > (rc = tidc_fwd_request(tidc, fwd_req, &tr_tidc_resp_handler, (void *)&resp_cookie))) {
223     tr_notice("Error from tidc_fwd_request, rc = %d.", rc);
224     tids_send_err_response(tids, orig_req, "Can't forward request to next hop TIDS");
225     tid_req_free(orig_req);
226     return -1;
227   }
228     
229   tid_req_free(orig_req);
230   return 0;
231 }
232
233 static int tr_tids_gss_handler(gss_name_t client_name, TR_NAME *gss_name,
234                         void *tr_in)
235 {
236   TR_RP_CLIENT *rp;
237   TR_INSTANCE *tr = (TR_INSTANCE *) tr_in;
238
239   if ((!client_name) || (!gss_name) || (!tr)) {
240     tr_debug("tr_tidc_gss_handler: Bad parameters.");
241     return -1;
242   }
243   
244   /* look up the RP client matching the GSS name */
245   if ((NULL == (rp = tr_rp_client_lookup(tr, gss_name)))) {
246     tr_debug("tr_tids_gss_handler: Unknown GSS name %s", gss_name->buf);
247     return -1;
248   }
249
250   /* Store the rp client in the TR_INSTANCE structure for now... 
251    * TBD -- fix me for new tasking model. */
252   (tr)->rp_gss = rp;
253   tr_debug("Client's GSS Name: %s", gss_name->buf);
254
255   return 0;
256 }
257
258 /* Strip trailing / from a path name.*/
259 static void remove_trailing_slash(char *s) {
260   size_t n;
261
262   n=strlen(s);
263   if(s[n-1]=='/') {
264     s[n-1]='\0';
265   }
266 }
267
268 /* command-line option setup */
269
270 /* argp global parameters */
271 const char *argp_program_bug_address=PACKAGE_BUGREPORT; /* bug reporting address */
272
273 /* doc strings */
274 static const char doc[]=PACKAGE_NAME " - Moonshot Trust Router";
275 static const char arg_doc[]=""; /* string describing arguments, if any */
276
277 /* define the options here. Fields are:
278  * { long-name, short-name, variable name, options, help description } */
279 static const struct argp_option cmdline_options[] = {
280     { "config-dir", 'c', "DIR", 0, "Specify configuration file location (default is current directory)"},
281     { NULL }
282 };
283
284 /* structure for communicating with option parser */
285 struct cmdline_args {
286   char *config_dir;
287 };
288
289 /* parser for individual options - fills in a struct cmdline_args */
290 static error_t parse_option(int key, char *arg, struct argp_state *state)
291 {
292   /* get a shorthand to the command line argument structure, part of state */
293   struct cmdline_args *arguments=state->input;
294
295   switch (key) {
296   case 'c':
297     if (arg == NULL) {
298       /* somehow we got called without an argument */
299       return ARGP_ERR_UNKNOWN;
300     }
301     arguments->config_dir=arg;
302     break;
303
304   default:
305     return ARGP_ERR_UNKNOWN;
306   }
307
308   return 0; /* success */
309 }
310
311 /* assemble the argp parser */
312 static struct argp argp = {cmdline_options, parse_option, arg_doc, doc};
313
314
315 int main (int argc, char *argv[])
316 {
317   TR_INSTANCE *tr = NULL;
318   struct dirent **cfg_files = NULL;
319   TR_CFG_RC rc = TR_CFG_SUCCESS;        /* presume success */
320   int err = 0, n = 0;
321   struct cmdline_args opts;
322
323   /* Use standalone logging */
324   tr_log_open();
325
326   /* parse command-line arguments */
327   /* set defaults */
328   opts.config_dir=".";
329
330   /* parse the command line*/
331   argp_parse(&argp, argc, argv, 0, 0, &opts);
332
333   /* process options */
334   remove_trailing_slash(opts.config_dir);
335
336   /* create a Trust Router instance */
337   if (NULL == (tr = tr_create())) {
338     tr_crit("Unable to create Trust Router instance, exiting.");
339     return 1;
340   }
341
342   /* find the configuration files -- n.b., tr_find_config_files()
343    * allocates memory to cfg_files which we must later free */
344   tr_debug("Reading configuration files from %s/", opts.config_dir);
345   n = tr_find_config_files(opts.config_dir, &cfg_files);
346   if (n <= 0) {
347     tr_crit("Can't locate configuration files, exiting.");
348     tr_free_config_file_list(n, &cfg_files);
349     exit(1);
350   }
351
352   if (TR_CFG_SUCCESS != tr_parse_config(tr, opts.config_dir, n, cfg_files)) {
353     tr_crit("Error decoding configuration information, exiting.");
354     tr_free_config_file_list(n, &cfg_files);
355     exit(1);
356   }
357   
358   /* we are now done with the config filenames, free those */
359   tr_free_config_file_list(n, &cfg_files);
360
361   /* apply initial configuration */
362   if (TR_CFG_SUCCESS != (rc = tr_apply_new_config(tr))) {
363     tr_crit("Error applying configuration, rc = %d.", rc);
364     exit(1);
365   }
366
367   /* print the loaded configuration */
368   tr_print_config(tr->active_cfg);
369
370   /* initialize the trust path query server instance */
371   if (0 == (tr->tids = tids_create ())) {
372     tr_crit("Error initializing Trust Path Query Server instance.");
373     exit(1);
374   }
375
376   /* start the trust path query server, won't return unless fatal error. */
377   if (0 != (err = tids_start(tr->tids, &tr_tids_req_handler, &tr_tids_gss_handler, tr->active_cfg->internal->hostname, tr->active_cfg->internal->tids_port, (void *)tr))) {
378     tr_crit("Error from Trust Path Query Server, err = %d.", err);
379     exit(err);
380   }
381
382   tids_destroy(tr->tids);
383   tr_destroy(tr);
384   exit(0);
385 }