Imported gnutls based branch.
[tinc] / src / protocol_auth.c
1 /*
2     protocol_auth.c -- handle the meta-protocol, authentication
3     Copyright (C) 1999-2003 Ivo Timmermans <ivo@o2w.nl>,
4                   2000-2003 Guus Sliepen <guus@sliepen.eu.org>
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20     $Id: protocol_auth.c,v 1.1.4.34 2003/12/22 11:04:16 guus Exp $
21 */
22
23 #include "system.h"
24
25 #include <gnutls/gnutls.h>
26 #include <gnutls/x509.h>
27
28 #include "avl_tree.h"
29 #include "conf.h"
30 #include "connection.h"
31 #include "edge.h"
32 #include "graph.h"
33 #include "logger.h"
34 #include "net.h"
35 #include "netutl.h"
36 #include "node.h"
37 #include "protocol.h"
38 #include "utils.h"
39 #include "xalloc.h"
40
41 bool send_ack(connection_t *c)
42 {
43         char buf[MAX_STRING_SIZE];
44         size_t len;
45         gnutls_x509_crt cert;
46         const gnutls_datum *cert_list;
47         int cert_list_size = 0, result;
48         char *p, *name;
49
50         cert_list = gnutls_certificate_get_peers(c->session, &cert_list_size);
51
52         if (!cert_list || !cert_list_size) {
53                 logger(LOG_ERR, _("No certificates from %s"), c->hostname);
54                 return false;
55         }
56
57         len = sizeof buf;
58         gnutls_x509_crt_init(&cert);
59         result = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)
60                 ?: gnutls_x509_crt_get_dn(cert, buf, &len);
61
62         if(result) {
63                 logger(LOG_ERR, _("Error importing certificate from %s: %s"), c->hostname, gnutls_strerror(errno));
64                 gnutls_x509_crt_deinit(cert);
65                 return false;
66         }
67
68         name = strstr(buf, "CN=");
69         if(!name) {
70                 logger(LOG_ERR, _("No name in certificate from %s"), c->hostname);
71                 gnutls_x509_crt_deinit(cert);
72                 return false;
73         }
74         name += 3;
75         for(p = name; *p && *p != ','; p++);
76         *p = '\0';
77
78         if(!check_id(name)) {
79                 logger(LOG_ERR, _("Invalid name from %s"), c->hostname);
80                 return false;
81         }
82
83         if(c->name) {
84                 if(strcmp(c->name, name)) {
85                         logger(LOG_ERR, _("Peer %s is %s instead of %s"), c->hostname, name, c->hostname);
86                         return false;
87                 }
88         } else {
89                 c->name = xstrdup(name);
90         }
91         
92         result = gnutls_certificate_verify_peers(c->session);
93
94         if(result) {
95                 if(result & GNUTLS_CERT_INVALID)
96                         logger(LOG_ERR, _("Certificate from %s (%s) invalid"), c->name, c->hostname);
97                 if(result & GNUTLS_CERT_REVOKED)
98                         logger(LOG_ERR, _("Certificate from %s (%s) revoked"), c->name, c->hostname);
99                 if(result & GNUTLS_CERT_SIGNER_NOT_FOUND)
100                         logger(LOG_ERR, _("Certificate from %s (%s) has no known signer"), c->name, c->hostname);
101                 if(result & GNUTLS_CERT_SIGNER_NOT_CA)
102                         logger(LOG_ERR, _("Certificate from %s (%s) has no CA as signer"), c->name, c->hostname);
103         }
104         
105         if(!c->config_tree) {
106                 init_configuration(&c->config_tree);
107
108                 if(!read_connection_config(c)) {
109                         logger(LOG_ERR, _("Peer %s had unknown identity (%s)"), c->hostname,
110                                    c->name);
111                         return false;
112                 }
113         }
114
115         /* ACK message contains rest of the information the other end needs
116            to create node_t and edge_t structures. */
117
118         struct timeval now;
119         bool choice;
120
121         cp();
122
123         /* Estimate weight */
124
125         gettimeofday(&now, NULL);
126         c->estimated_weight = (now.tv_sec - c->start.tv_sec) * 1000 + (now.tv_usec - c->start.tv_usec) / 1000;
127
128         /* Check some options */
129
130         if((get_config_bool(lookup_config(c->config_tree, "IndirectData"), &choice) && choice) || myself->options & OPTION_INDIRECT)
131                 c->options |= OPTION_INDIRECT;
132
133         if((get_config_bool(lookup_config(c->config_tree, "TCPOnly"), &choice) && choice) || myself->options & OPTION_TCPONLY)
134                 c->options |= OPTION_TCPONLY | OPTION_INDIRECT;
135
136         if((get_config_bool(lookup_config(c->config_tree, "PMTUDiscovery"), &choice) && choice) || myself->options & OPTION_PMTU_DISCOVERY)
137                 c->options |= OPTION_PMTU_DISCOVERY;
138
139         get_config_int(lookup_config(c->config_tree, "Weight"), &c->estimated_weight);
140
141         return send_request(c, "%d %s %d %lx", ACK, myport, c->estimated_weight, c->options);
142 }
143
144 static void send_everything(connection_t *c)
145 {
146         avl_node_t *node, *node2;
147         node_t *n;
148         subnet_t *s;
149         edge_t *e;
150
151         /* Send all known subnets and edges */
152
153         if(tunnelserver) {
154                 for(node = myself->subnet_tree->head; node; node = node->next) {
155                         s = node->data;
156                         send_add_subnet(c, s);
157                 }
158
159                 return;
160         }
161
162         for(node = node_tree->head; node; node = node->next) {
163                 n = node->data;
164
165                 for(node2 = n->subnet_tree->head; node2; node2 = node2->next) {
166                         s = node2->data;
167                         send_add_subnet(c, s);
168                 }
169
170                 for(node2 = n->edge_tree->head; node2; node2 = node2->next) {
171                         e = node2->data;
172                         send_add_edge(c, e);
173                 }
174         }
175 }
176
177 bool ack_h(connection_t *c)
178 {
179         char hisport[MAX_STRING_SIZE];
180         char *hisaddress, *dummy;
181         int weight, mtu;
182         long int options;
183         node_t *n;
184
185         cp();
186
187         if(sscanf(c->buffer, "%*d " MAX_STRING " %d %lx", hisport, &weight, &options) != 3) {
188                 logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ACK", c->name,
189                            c->hostname);
190                 return false;
191         }
192
193         /* Check if we already have a node_t for him */
194
195         n = lookup_node(c->name);
196
197         if(!n) {
198                 n = new_node();
199                 n->name = xstrdup(c->name);
200                 node_add(n);
201         } else {
202                 if(n->connection) {
203                         /* Oh dear, we already have a connection to this node. */
204                         ifdebug(CONNECTIONS) logger(LOG_DEBUG, _("Established a second connection with %s (%s), closing old connection"),
205                                            n->name, n->hostname);
206                         terminate_connection(n->connection, false);
207                         /* Run graph algorithm to purge key and make sure up/down scripts are rerun with new IP addresses and stuff */
208                         graph();
209                 }
210         }
211
212         n->connection = c;
213         c->node = n;
214         c->options |= options;
215
216         if(get_config_int(lookup_config(c->config_tree, "PMTU"), &mtu) && mtu < n->mtu)
217                 n->mtu = mtu;
218
219         if(get_config_int(lookup_config(myself->connection->config_tree, "PMTU"), &mtu) && mtu < n->mtu)
220                 n->mtu = mtu;
221
222         /* Activate this connection */
223
224         c->allow_request = ALL;
225         c->status.active = true;
226
227         ifdebug(CONNECTIONS) logger(LOG_NOTICE, _("Connection with %s (%s) activated"), c->name,
228                            c->hostname);
229
230         /* Send him everything we know */
231
232         send_everything(c);
233
234         /* Create an edge_t for this connection */
235
236         c->edge = new_edge();
237         cp();
238         c->edge->from = myself;
239         c->edge->to = n;
240         sockaddr2str(&c->address, &hisaddress, &dummy);
241         c->edge->address = str2sockaddr(hisaddress, hisport);
242         free(hisaddress);
243         free(dummy);
244         c->edge->weight = (weight + c->estimated_weight) / 2;
245         c->edge->connection = c;
246         c->edge->options = c->options;
247
248         edge_add(c->edge);
249
250         /* Notify everyone of the new edge */
251
252         if(tunnelserver)
253                 send_add_edge(c, c->edge);
254         else
255                 send_add_edge(broadcast, c->edge);
256
257         /* Run MST and SSSP algorithms */
258
259         graph();
260
261         return true;
262 }