64ddf3cacabdb712f5cc89ecf19b2c7781aeccd5
[trust_router.git] / trp / trp_conn.c
1 /*
2  * Copyright (c) 2016, 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 <gsscon.h>
36 #include <gssapi.h>
37 #include <fcntl.h>
38 #include <talloc.h>
39 #include <unistd.h>
40
41 #include <tr_debug.h>
42 #include <trp_internal.h>
43
44 /* Threading note: mutex lock is only used for protecting get_status() and set_status().
45  * If needed, locking for other operations (notably adding/removing connections) must be managed
46  * by whomever is holding on to the connection list. */
47
48 int trp_connection_lock(TRP_CONNECTION *conn)
49 {
50   return pthread_mutex_lock(&(conn->mutex));
51 }
52
53 int trp_connection_unlock(TRP_CONNECTION *conn)
54 {
55   return pthread_mutex_unlock(&(conn->mutex));
56 }
57
58 int trp_connection_get_fd(TRP_CONNECTION *conn)
59 {
60   return conn->fd;
61 }
62
63 void trp_connection_set_fd(TRP_CONNECTION *conn, int fd)
64 {
65   conn->fd=fd;
66 }
67
68 /* we use the gss name of the peer to identify it */
69 static TRP_RC trp_connection_set_peer(TRP_CONNECTION *conn)
70 {
71   OM_uint32 major_status=0;
72   OM_uint32 minor_status=0;
73   gss_name_t source_name=GSS_C_NO_NAME;
74   gss_name_t target_name=GSS_C_NO_NAME;
75   gss_buffer_desc peer_display_name={0,NULL};
76   int local=0;
77
78   major_status=gss_inquire_context(&minor_status,
79                                    *trp_connection_get_gssctx(conn),
80                                   &source_name,
81                                   &target_name,
82                                    NULL,
83                                    NULL,
84                                    NULL,
85                                   &local,
86                                    NULL);
87
88   if (major_status != GSS_S_COMPLETE) {
89     tr_err("trp_connection_set_peer: unable to identify GSS peer.");
90     if (source_name!=GSS_C_NO_NAME)
91       gss_release_name(&minor_status, &source_name);
92     if (target_name!=GSS_C_NO_NAME)
93       gss_release_name(&minor_status, &target_name);
94     return TRP_ERROR;
95   }
96
97   if (local) {
98     /* we are the source, peer is the target */
99     major_status=gss_display_name(&minor_status, target_name, &peer_display_name, NULL);
100   } else {
101     /* we are the target, peer is the source */
102     major_status=gss_display_name(&minor_status, source_name, &peer_display_name, NULL);
103   }
104   gss_release_name(&minor_status, &source_name);
105   gss_release_name(&minor_status, &target_name);
106
107   conn->peer=tr_new_name(peer_display_name.value);
108   if (conn->peer==NULL)
109     tr_err("trp_connection_set_peer: unable to allocate peer name.");
110   else {
111     if (conn->peer->len != peer_display_name.length) {
112       tr_err("trp_connection_set_peer: error converting GSS display name to TR_NAME.");
113       tr_free_name(conn->peer);
114       conn->peer=NULL;
115     }
116   }
117   gss_release_buffer(&minor_status, &peer_display_name);
118
119   if (conn->peer==NULL)
120     return TRP_ERROR;
121
122   tr_debug("trp_connection_set_peer: set peer for %p to %.*s (%p).", conn, conn->peer->len, conn->peer->buf, conn->peer);
123   return TRP_SUCCESS;
124 }
125
126 TR_NAME *trp_connection_get_peer(TRP_CONNECTION *conn)
127 {
128   return conn->peer;
129 }
130
131 TR_NAME *trp_connection_get_gssname(TRP_CONNECTION *conn)
132 {
133   return conn->gssname;
134 }
135
136 void trp_connection_set_gssname(TRP_CONNECTION *conn, TR_NAME *gssname)
137 {
138   conn->gssname=gssname;
139 }
140
141 gss_ctx_id_t *trp_connection_get_gssctx(TRP_CONNECTION *conn)
142 {
143   return conn->gssctx;
144 }
145
146 void trp_connection_set_gssctx(TRP_CONNECTION *conn, gss_ctx_id_t *gssctx)
147 {
148   conn->gssctx=gssctx;
149 }
150
151 TRP_CONNECTION_STATUS trp_connection_get_status(TRP_CONNECTION *conn)
152 {
153   TRP_CONNECTION_STATUS status=TRP_CONNECTION_UNKNOWN;
154   trp_connection_lock(conn);
155   status=conn->status;
156   trp_connection_unlock(conn);
157   return status;
158 }
159
160 static void trp_connection_set_status(TRP_CONNECTION *conn, TRP_CONNECTION_STATUS status)
161 {
162   TRP_CONNECTION_STATUS old_status=TRP_CONNECTION_UNKNOWN;
163   trp_connection_lock(conn);
164   old_status=conn->status;
165   conn->status=status;
166   trp_connection_unlock(conn);
167   if ((status!=old_status) && (conn->status_change_cb!=NULL))
168       conn->status_change_cb(conn, conn->status_change_cookie);
169 }
170
171 pthread_t *trp_connection_get_thread(TRP_CONNECTION *conn)
172 {
173   return conn->thread;
174 }
175
176 void trp_connection_set_thread(TRP_CONNECTION *conn, pthread_t *thread)
177 {
178   conn->thread=thread;
179 }
180
181 TRP_CONNECTION *trp_connection_get_next(TRP_CONNECTION *conn)
182 {
183   return conn->next;
184 }
185
186 static void trp_connection_set_next(TRP_CONNECTION *conn, TRP_CONNECTION *next)
187 {
188   conn->next=next;
189 }
190
191 /* Ok to call more than once; guarantees connection no longer in the list. Does not free removed element.
192  * Returns handle to new list, you must replace your old handle on the list with this.  */
193 TRP_CONNECTION *trp_connection_remove(TRP_CONNECTION *conn, TRP_CONNECTION *remove)
194 {
195   TRP_CONNECTION *cur=conn;
196   TRP_CONNECTION *last=NULL;
197
198   if (cur==NULL)
199     return NULL;
200
201   /* first element is a special case */
202   if (cur==remove) {
203     conn=trp_connection_get_next(cur); /* advance list head */
204   } else {
205     /* it was not the first element */
206     last=cur;
207     cur=trp_connection_get_next(cur);
208     while (cur!=NULL) {
209       if (cur==remove) {
210         trp_connection_set_next(last, trp_connection_get_next(cur));
211         break;
212       }
213       last=cur;
214       cur=trp_connection_get_next(cur);
215     }
216   }
217   return conn;
218 }
219
220 static TRP_CONNECTION *trp_connection_get_tail(TRP_CONNECTION *conn)
221 {
222   while((conn!=NULL)&&(trp_connection_get_next(conn)!=NULL))
223     conn=trp_connection_get_next(conn);
224   return conn;
225 }
226
227 void trp_connection_append(TRP_CONNECTION *conn, TRP_CONNECTION *new)
228 {
229   trp_connection_set_next(trp_connection_get_tail(conn), new);
230 }
231
232 static void trp_connection_mutex_init(TRP_CONNECTION *conn)
233 {
234   pthread_mutex_init(&(conn->mutex), NULL);
235 }
236
237 /* talloc destructor for a connection: ensures connection is closed, memory freed */
238 static int trp_connection_destructor(void *object)
239 {
240   TRP_CONNECTION *conn=talloc_get_type_abort(object, TRP_CONNECTION); /* aborts on wrong type */
241   if ((trp_connection_get_status(conn)!=TRP_CONNECTION_CLOSED)
242      && (trp_connection_get_fd(conn)!=-1))
243     close(trp_connection_get_fd(conn));
244   if (conn->peer!=NULL)
245     tr_free_name(conn->peer);
246   if (conn->gssname!=NULL)
247     tr_free_name(conn->gssname);
248   return 0;
249 }
250
251 TRP_CONNECTION *trp_connection_new(TALLOC_CTX *mem_ctx)
252 {
253   TRP_CONNECTION *new_conn=talloc(mem_ctx, TRP_CONNECTION);
254   gss_ctx_id_t *gssctx=NULL;
255   pthread_t *thread=NULL;
256   
257
258   if (new_conn != NULL) {
259     trp_connection_set_next(new_conn, NULL);
260     trp_connection_set_fd(new_conn, -1);
261     trp_connection_set_gssname(new_conn, NULL);
262     trp_connection_mutex_init(new_conn);
263     new_conn->peer=NULL; /* no true set function for this */
264     new_conn->status_change_cb=NULL;
265     new_conn->status_change_cookie=NULL;
266     new_conn->status=TRP_CONNECTION_CLOSED;
267
268     thread=talloc(new_conn, pthread_t);
269     if (thread==NULL) {
270       talloc_free(new_conn);
271       return NULL;
272     }
273     trp_connection_set_thread(new_conn, thread);
274
275     gssctx=talloc(new_conn, gss_ctx_id_t);
276     if (gssctx==NULL) {
277       talloc_free(new_conn);
278       return NULL;
279     }
280     trp_connection_set_gssctx(new_conn, gssctx);
281     talloc_set_destructor((void *)new_conn, trp_connection_destructor);
282   }
283   return new_conn;
284 }
285
286 void trp_connection_free(TRP_CONNECTION *conn)
287 {
288   talloc_free(conn);
289 }
290
291 void trp_connection_close(TRP_CONNECTION *conn)
292 {
293   if ((conn->status!=TRP_CONNECTION_DOWN) && (conn->fd>0))
294     close(trp_connection_get_fd(conn));
295   trp_connection_set_fd(conn, -1);
296   trp_connection_set_status(conn, TRP_CONNECTION_DOWN);
297 }
298
299 /* returns 0 on authorization success, 1 on failure, or -1 in case of error */
300 int trp_connection_auth(TRP_CONNECTION *conn, TRP_AUTH_FUNC auth_callback, void *callback_data)
301 {
302   int rc = 0;
303   int auth, autherr = 0;
304   gss_buffer_desc nameBuffer = {0, NULL};
305   gss_ctx_id_t *gssctx=trp_connection_get_gssctx(conn);
306
307   nameBuffer.length = trp_connection_get_gssname(conn)->len;
308   nameBuffer.value = tr_name_strdup(trp_connection_get_gssname(conn));
309
310   tr_debug("trp_connection_auth: beginning passive authentication");
311   if (trp_connection_get_status(conn)!=TRP_CONNECTION_AUTHORIZING)
312     tr_warning("trp_connection_auth: warning: connection was not in TRP_CONNECTION_AUTHORIZING state.");
313
314   rc = gsscon_passive_authenticate(trp_connection_get_fd(conn), nameBuffer, gssctx, auth_callback, callback_data);
315   gss_release_buffer(NULL, &nameBuffer);
316   if (rc!=0) {
317     tr_debug("trp_connection_auth: Error from gsscon_passive_authenticate(), rc = 0x%08X.", rc);
318     trp_connection_set_status(conn, TRP_CONNECTION_DOWN);
319     return -1;
320   }
321
322   tr_debug("trp_connection_auth: beginning second stage authentication");
323   if (rc = gsscon_authorize(*gssctx, &auth, &autherr)) {
324     tr_debug("trp_connection_auth: Error from gsscon_authorize, rc = %d, autherr = %d.", 
325              rc, autherr);
326     trp_connection_set_status(conn, TRP_CONNECTION_DOWN);
327     return -1;
328   }
329
330   trp_connection_set_peer(conn);
331   trp_connection_set_status(conn, TRP_CONNECTION_UP);
332
333   if (auth)
334     tr_debug("trp_connection_auth: Connection authenticated, fd = %d.", trp_connection_get_fd(conn));
335   else
336     tr_debug("trp_connection_auth: Authentication failed, fd = %d.", trp_connection_get_fd(conn));
337
338   return !auth;
339 }
340
341 /* Accept connection */
342 TRP_CONNECTION *trp_connection_accept(TALLOC_CTX *mem_ctx, int listen, TR_NAME *gssname)
343 {
344   int conn_fd=-1;
345   TRP_CONNECTION *conn=NULL;
346
347   conn_fd = accept(listen, NULL, NULL);
348
349   if (0 > conn_fd) {
350     tr_notice("trp_connection_accept: accept() returned error.");
351     return NULL;
352   }
353   conn=trp_connection_new(mem_ctx);
354   trp_connection_set_fd(conn, conn_fd);
355   trp_connection_set_gssname(conn, gssname);
356   trp_connection_set_status(conn, TRP_CONNECTION_AUTHORIZING);
357   return conn;
358 }
359
360 /* Initiate connection */
361 TRP_RC trp_connection_initiate(TRP_CONNECTION *conn, char *server, unsigned int port)
362 {
363   int err = 0;
364   int fd=-1;
365   unsigned int use_port=0;
366
367   if (0 == port)
368     use_port = TRP_PORT;
369   else 
370     use_port = port;
371
372   if (conn==NULL) {
373     tr_err("trp_connection_initiate: null TRP_CONNECTION");
374     return TRP_BADARG;
375   }
376
377   tr_debug("trp_connection_initiate: opening GSS connection to %s:%d",
378            server,
379            use_port);
380   err = gsscon_connect(server,
381                        use_port,
382                        "trustrouter",
383                       &fd,
384                        trp_connection_get_gssctx(conn));
385   if (err) {
386     tr_err("trp_connection_initiate: connection failed.");
387     return TRP_ERROR;
388   } else {
389     tr_debug("trp_connection_initiate: connected.");
390     trp_connection_set_fd(conn, fd);
391     if (trp_connection_set_peer(conn)!=TRP_SUCCESS) {
392       tr_err("trp_connection_initiate: error setting peer gssname.");
393       trp_connection_close(conn);
394       return TRP_ERROR;
395     }
396     trp_connection_set_status(conn, TRP_CONNECTION_UP);
397     return TRP_SUCCESS;
398   }
399 }