Separate TRP from main trust router code.
[trust_router.git] / trp / trps.c
1 #include <fcntl.h>
2 #include <event2/event.h>
3 #include <talloc.h>
4 #include <errno.h>
5 #include <unistd.h>
6
7 #include <gsscon.h>
8 #include <tr_rp.h>
9 #include <tr_event.h>
10 #include <tr_debug.h>
11 #include <trp_internal.h>
12
13 /* connections and sets of connections */
14 TRPS_CONNECTION *trps_connection_new(TALLOC_CTX *mem_ctx)
15 {
16   TRPS_CONNECTION *new_conn=talloc_new(mem_ctx, TRPS_CONNECTION);
17
18   if (new_conn != NULL) {
19     new_conn->conn=-1;
20     new_conn->gssctx=0;
21   }
22
23   return new_conn;
24 }
25
26
27
28 TRPS_CONNECTION_SET *trps_connection_set_new(TALLOC_CTX *mem_ctx)
29 {
30   TRPS_CONNECTION_SET *new_set=talloc_new(mem_ctx, TRPS_CONNECTION_SET);
31   int ii=0;
32
33   if (new_set != NULL) {
34     new_set->nconn=0;
35     for(ii=0; ii<TRPS_CONNECTIONS_MAX; ii++)
36       new_set->conn[ii]=0;
37   }
38
39   return new_set;
40 }
41
42 TRPS_ERR trps_connection_set_add(TRPS_CONNECTION_SET *tcs, TRPS_CONNECTION *new_conn)
43 {
44   TR_ERR err=TRPS_ERR_OK;
45
46   if (tcs->nconn < TRPS_CONNECTIONS_MAX) {
47     tcs->conn[tcs->nconn]=new_conn;
48     talloc_steal(tcs, new_conn);
49     tcs->nconn++;
50   } else {
51     err=TRPS_ERR_MAX_CONN;
52   }
53
54   return err;
55 }
56
57 int trps_connection_set_del(TRPS_CONNECTION_SET *tcs, TRPS_CONNECTION *conn)
58 {
59   /* not implemented */
60   return TRPS_ERR_UNKNOWN;
61 }
62
63 int trps_connection_set_len(TRPS_CONNECTION_SET *tcs)
64 {
65   return tcs->nconn;
66 }
67
68
69
70
71 TRPS_INSTANCE *trps_create (TALLOC_CTX *mem_ctx)
72 {
73   return talloc_zero(mem_ctx, TRPS_INSTANCE);
74 }
75
76 void trps_destroy (TRPS_INSTANCE *trps)
77 {
78   if (trps)
79     talloc_free(trps);
80 }
81
82
83 int trps_send_msg (TRPS_INSTANCE *trps,
84                    int conn,
85                    gss_ctx_id_t gssctx,
86                    const char *msg_content)
87 {
88   int err=0;
89   int rc=0;
90
91   /* Send the request over the connection */
92   if (err = gsscon_write_encrypted_token (conn,
93                                           gssctx,
94                                           msg_content, 
95                                           strlen(msg_content))) {
96     tr_err( "trps_send_msg: Error sending message over connection.\n");
97     rc = -1;
98   }
99
100   return rc;
101 }
102
103 static int trps_listen (TRPS_INSTANCE *trps, int port) 
104 {
105     int rc = 0;
106     int conn = -1;
107     int optval = 1;
108
109     union {
110       struct sockaddr_storage storage;
111       struct sockaddr_in in4;
112     } addr;
113
114     struct sockaddr_in *saddr = (struct sockaddr_in *) &addr.in4;
115
116     saddr->sin_port = htons (port);
117     saddr->sin_family = AF_INET;
118     saddr->sin_addr.s_addr = INADDR_ANY;
119
120     if (0 > (conn = socket (AF_INET, SOCK_STREAM, 0)))
121       return conn;
122
123     setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
124
125     if (0 > (rc = bind (conn, (struct sockaddr *) saddr, sizeof(struct sockaddr_in))))
126       return rc;
127
128     if (0 > (rc = listen(conn, 512)))
129       return rc;
130
131     tr_debug("trps_listen: TRP Server listening on port %d", port);
132     return conn;
133 }
134
135 /* returns EACCES if authorization is denied */
136 static int trps_auth_cb(gss_name_t clientName, gss_buffer_t displayName,
137                         void *data)
138 {
139   TRPS_INSTANCE *inst = (TRPS_INSTANCE *)data;
140   TR_NAME name ={(char *) displayName->value,
141                  displayName->length};
142   int result=0;
143
144   if (0!=inst->auth_handler(clientName, &name, inst->cookie)) {
145     tr_debug("trps_auth_cb: client '%.*s' denied authorization.", name.len, name.buf);
146     result=EACCES; /* denied */
147   }
148
149   return result;
150 }
151
152 /* returns 0 on authorization success, 1 on failure, or -1 in case of error */
153 static int trps_auth_connection (TRPS_INSTANCE *inst,
154                                  int conn,
155                                  gss_ctx_id_t *gssctx)
156 {
157   int rc = 0;
158   int auth, autherr = 0;
159   gss_buffer_desc nameBuffer = {0, NULL};
160   char *name = 0;
161   int nameLen = 0;
162
163   nameLen = asprintf(&name, "trustrouter@%s", inst->hostname);
164   nameBuffer.length = nameLen;
165   nameBuffer.value = name;
166   
167   if (rc = gsscon_passive_authenticate(conn, nameBuffer, gssctx, trps_auth_cb, inst)) {
168     tr_debug("trps_auth_connection: Error from gsscon_passive_authenticate(), rc = %d.", rc);
169     return -1;
170   }
171
172   if (rc = gsscon_authorize(*gssctx, &auth, &autherr)) {
173     tr_debug("trps_auth_connection: Error from gsscon_authorize, rc = %d, autherr = %d.", 
174             rc, autherr);
175     return -1;
176   }
177
178   if (auth)
179     tr_debug("trps_auth_connection: Connection authenticated, conn = %d.", conn);
180   else
181     tr_debug("trps_auth_connection: Authentication failed, conn %d.", conn);
182
183   return !auth;
184 }
185
186 static int trps_read_message (TRPS_INSTANCE *trps, int conn, gss_ctx_id_t *gssctx, char **msg)
187 {
188   int err;
189   char *buf;
190   size_t buflen = 0;
191
192   if (err = gsscon_read_encrypted_token(conn, *gssctx, &buf, &buflen)) {
193     if (buf)
194       free(buf);
195     return -1;
196   }
197
198   tr_debug("trps_read_request(): Request Received, %u bytes.", (unsigned) buflen);
199   tr_debug("trps_read_request(): %.*s", buflen, buf);
200
201   *msg=talloc_strndup(NULL, buf, buflen); /* no context owns this! */
202   free(buf);
203   return buflen;
204 }
205
206 static int trps_get_listener(TRPS_INSTANCE *trps,
207                              TRPS_REQ_FUNC *req_handler,
208                              trps_auth_func *auth_handler,
209                              const char *hostname,
210                              unsigned int port,
211                              void *cookie)
212 {
213   int listen = -1;
214
215   if (0 > (listen = trps_listen(trps, port))) {
216     char errbuf[256];
217     if (0 == strerror_r(errno, errbuf, 256)) {
218       tr_debug("trps_get_listener: Error opening port %d: %s.", port, errbuf);
219     } else {
220       tr_debug("trps_get_listener: Unknown error openining port %d.", port);
221     }
222   } 
223
224   if (listen > 0) {
225     /* opening port succeeded */
226     tr_debug("trps_get_listener: Opened port %d.", port);
227     
228     /* make this socket non-blocking */
229     if (0 != fcntl(listen, F_SETFL, O_NONBLOCK)) {
230       tr_debug("trps_get_listener: Error setting O_NONBLOCK.");
231       close(listen);
232       listen=-1;
233     }
234   }
235
236   if (listen > 0) {
237     /* store the caller's request handler & cookie */
238     trps->req_handler = req_handler;
239     trps->auth_handler = auth_handler;
240     trps->hostname = talloc_strdup(trps, hostname);
241     trps->port = port;
242     trps->cookie = cookie;
243   }
244
245   return listen;
246 }
247
248
249 /* Accept and process a connection on a port opened with trps_get_listener().
250  * Returns the socket FD, or -1 in case of error. */
251 int trps_accept(TRPS_INSTANCE *trps, int listen)
252 {
253   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
254   int conn=-1;
255   gss_ctx_id_t gssctx;
256   TRPS_CONNECTION *trps_conn=0;
257   TRPS_ERR trps_err=TRPS_ERR_OK;
258
259   conn = accept(listen, NULL, NULL);
260
261   if (0 > conn) {
262     perror("Error from TRP Server accept()");
263     goto cleanup;
264   }
265
266   /* establish a GSS context */
267   if (trps_auth_connection(trps, conn, &gssctx)) {
268     tr_notice("trps_accept: Error authorizing TID Server connection.");
269     close(conn); /* did not work */
270     conn=-1;
271     goto cleanup;
272   } 
273
274   tr_notice("trps_accept: Connection authorized!");
275
276     /* add this to the list of connections */
277   trps_conn=trps_connection_new(tmp_ctx);
278   if (trps_conn==NULL) {
279     tr_debug("trps_handle_connection: Could not allocate TRPS connection.");
280     close(conn);
281     conn=-1;
282     goto cleanup;
283   }
284
285   trps_err=trps_connection_set_add(trps->connections, trps_conn); /* handles talloc steal */
286   if (trps_err != TRPS_ERR_OK) {
287     tr_debug("trps_handle_connection: Error adding connection to set (trps_err=%d)", trps_err);
288     close(conn);
289     conn=-1;
290     goto cleanup;
291   }
292
293   /* GSS context established, saved to the TRPS instance---success! */
294
295 cleanup:
296   talloc_free(tmp_ctx);
297   return conn;
298 }
299
300
301
302 /* old cruft */
303 #if 0
304 static gss_ctx_id_t trps_establish_gss_context (TRPS_INSTANCE *trps, int conn)
305 {
306   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
307   gss_ctx_id_t gssctx = GSS_C_NO_CONTEXT;
308   char *msg_rec=NULL;
309   int msg_len = 0;
310   int rc=0;
311
312   if (trps_auth_connection(trps, conn, &gssctx))
313     tr_notice("trps_establish_gss_context: Error authorizing TID Server connection.");
314   else:
315     tr_notice("trps_establish_gss_context: Connection authorized!");
316   return gssctx;
317
318   msg_len = trps_read_message(trps, conn, &gssctx, &msg_rec);
319   talloc_steal(tmp_ctx, msg_rec); /* get this in our context */
320   if (0 > msg_len) {
321     tr_debug("trps_handle_connection: Error from trps_read_message()");
322     goto cleanup;
323   }
324   
325   tr_debug("trps_handle_connection: msg_len=%d", msg_len);
326   reply=talloc_asprintf(tmp_ctx, "TRPS heard: %.*s", msg_len, msg_rec);
327   if (0 > (rc = trps_send_msg(trps, conn, gssctx, reply))) {
328     tr_debug("trps_handle_connection: Error from trps_send_message(), rc = %d.", rc);
329   }
330
331 cleanup:
332   talloc_free(tmp_ctx);
333   return conn;
334 }
335 #endif