encode_uint32 second parameter should be uint32_t, not unsigned long
[git-daemon2.git] / client / main.c
blob8a5622c539dafec478a69fca09147e9f524d3b29
1 /*
2 * Copyright (C) Ilari Liusvaara 2009-2010
4 * This code is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8 #include "user.h"
9 #include "srp_askpass.h"
10 #include "keypairs.h"
11 #include "hostkey.h"
12 #include "connect.h"
13 #include "ssh.h"
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <unistd.h>
19 #include <fcntl.h>
20 #include <poll.h>
21 #include <gnutls/gnutls.h>
22 #include <signal.h>
23 #include "git-compat-util.h"
25 volatile sig_atomic_t in_handler = 0;
26 volatile sig_atomic_t sigusr1_flag = 0;
27 struct user *debug_for = NULL;
28 int verbose = 0;
29 static int socket_fd;
31 static void sigusr1_handler(int x)
33 x = 0;
34 if (in_handler)
35 sigusr1_flag = 1;
36 else
37 user_debug(debug_for, stderr);
40 struct parsed_addr
42 char *protocol; /* Protocol part */
43 char *user; /* User part, NULL if no user */
44 char *host; /* Hostname */
45 char *uservid; /* Unique server ID */
46 char *port; /* Port as string, NULL if no port */
47 char *path; /* Path part. */
48 char *vhost_header; /* vhost header to send */
49 char *transport_proto; /* Transport protocol. */
50 char *address_space; /* Address space to use. May be NULL */
53 void append_uniq_address(char *buffer, struct parsed_addr *_addr,
54 int default_flag)
56 char *ptr;
57 ptr = strchr(_addr->host, '~');
58 if (!ptr)
59 ptr = _addr->host;
60 else
61 ptr++;
62 if (strchr(ptr, ':'))
63 strcat(buffer, "[");
64 strcat(buffer, ptr);
65 if (strchr(_addr->host, ':'))
66 strcat(buffer, "]");
67 if(!default_flag) {
68 strcat(buffer, ":");
69 strcat(buffer, _addr->port);
73 struct parsed_addr parse_address(const char *addr)
75 struct parsed_addr _addr;
76 const char *proto_end;
77 const char *path_start;
78 const char *uhp_start;
79 const char *uhp_delim;
80 const char *orig_addr = addr;
81 const char *proto_sep;
82 size_t addrlen;
83 int nondefault_port = 0;
84 size_t vhost_len;
86 _addr.transport_proto = xstrdup("tcp");
87 _addr.address_space = NULL;
89 addrlen = strlen(addr);
91 proto_end = strchr(addr, ':');
92 if (!proto_end)
93 die("URL '%s': No ':' to end protocol.", orig_addr);
95 _addr.protocol = xstrndup(addr, proto_end - addr);
96 if (strncmp(proto_end, "://", 3))
97 die("URL '%s': No '://' to end protocol.", orig_addr);
99 uhp_start = proto_end + 3;
101 /* Figure out the user if any. */
102 uhp_delim = strpbrk(uhp_start, "@[:/");
104 if (*uhp_delim == '@') {
105 _addr.user = xstrndup(uhp_start,
106 uhp_delim - uhp_start);
107 uhp_start = uhp_delim + 1;
108 } else {
109 _addr.user = NULL;
112 /* Figure out host. */
113 if (*uhp_start == '[') {
114 uhp_delim = strpbrk(uhp_start, "]");
115 if (uhp_delim) {
116 _addr.host = xstrndup(uhp_start + 1,
117 uhp_delim - uhp_start - 1);
118 if (uhp_delim[1] != ':' && uhp_delim[1] != '/')
119 die("URL '%s': Expected port or path after hostname",
120 orig_addr);
121 uhp_start = uhp_delim + 1;
122 } else
123 die("URL '%s': Hostname has '[' without matching ']'",
124 orig_addr);
125 } else {
126 uhp_delim = strpbrk(uhp_start, "[:/");
127 if (*uhp_delim == '[')
128 die("URL '%s': Unexpected '['", orig_addr);
129 _addr.host = xstrndup(uhp_start, uhp_delim - uhp_start);
130 uhp_start = uhp_delim;
133 _addr.uservid = NULL;
134 proto_sep = strchr(_addr.host, '@');
135 if (proto_sep && proto_sep != _addr.host)
137 char *old_host;
138 old_host = _addr.host;
139 _addr.uservid = xstrndup(_addr.host, proto_sep - _addr.host);
140 _addr.host = xstrdup(proto_sep + 1);
141 free(old_host);
145 proto_sep = strchr(_addr.host, '~');
146 if (proto_sep)
148 free(_addr.transport_proto);
149 free(_addr.address_space);
151 const char *ptr2;
152 char *old_host;
153 old_host = _addr.host;
154 ptr2 = strchr(old_host, '/');
155 if (ptr2 >= proto_sep)
156 ptr2 = NULL;
158 if (ptr2) {
159 _addr.transport_proto = xstrndup(_addr.host,
160 ptr2 - old_host);
161 _addr.address_space = xstrndup(ptr2 + 1,
162 proto_sep - ptr2 - 1);
163 } else if (proto_sep == old_host + 4 &&
164 !strncmp(old_host, "unix", 4)) {
165 _addr.transport_proto = xstrdup("<N/A>");
166 _addr.address_space = xstrdup("unix");
167 } else {
168 char suffix = '\0';
169 if(proto_sep > old_host)
170 suffix = proto_sep[-1];
171 if(suffix == '4') {
172 _addr.transport_proto = xstrndup(
173 old_host, proto_sep - old_host - 1);
174 _addr.address_space = xstrdup("ipv4");
175 } else if(suffix == '6') {
176 _addr.transport_proto = xstrndup(
177 old_host, proto_sep - old_host - 1);
178 _addr.address_space = xstrdup("ipv6");
179 } else if(suffix == '_') {
180 _addr.transport_proto = xstrndup(
181 old_host, proto_sep - old_host - 1);
182 _addr.address_space = NULL;
183 } else {
184 _addr.transport_proto = xstrndup(
185 old_host, proto_sep - old_host);
186 _addr.address_space = NULL;
189 _addr.host = xstrdup(proto_sep + 1);
190 free(old_host);
193 path_start = strchr(uhp_start, '/');
194 if (!path_start)
195 die("URL '%s': No '/' to start path", orig_addr);
197 _addr.path = xstrndup(path_start, addrlen - (path_start - addr));
199 if (*uhp_start == ':')
200 _addr.port = xstrndup(uhp_start + 1,
201 path_start - uhp_start - 1);
202 else
203 _addr.port = NULL;
205 if (!*_addr.host)
206 die("URL '%s': Empty hostname not allowed", orig_addr);
208 if (strcmp(_addr.protocol, "gits") && strcmp(_addr.protocol, "tls") &&
209 strcmp(_addr.protocol, "git")) {
210 die("Unknown protocol %s://", _addr.protocol);
213 if (!strcmp(_addr.protocol, "git") && _addr.user) {
214 die("git:// does not support users");
217 if (!strcmp(_addr.protocol, "git") && _addr.uservid) {
218 die("git:// does not support unique server identitifier");
221 if (_addr.port) {
222 nondefault_port = 1;
223 } else if (!strcmp(_addr.protocol, "gits")) {
224 _addr.port = xstrndup("git", 3);
225 } else if (!strcmp(_addr.protocol, "git")) {
226 _addr.port = xstrndup("git", 3);
227 } else if (!strcmp(_addr.protocol, "tls")) {
228 _addr.port = xstrndup("gits", 4);
231 /* 8 is for host=[]:\0 */
232 vhost_len = 9 + strlen(_addr.host) + strlen(_addr.port);
233 _addr.vhost_header = xmalloc(vhost_len);
235 strcpy(_addr.vhost_header, "host=");
236 append_uniq_address(_addr.vhost_header, &_addr, !nondefault_port);
238 if (verbose) {
239 fprintf(stderr, "Protocol: %s\n", _addr.protocol);
240 fprintf(stderr, "User: %s\n", _addr.user ?
241 _addr.user : "<not set>");
242 fprintf(stderr, "Host: %s\n", _addr.host);
243 fprintf(stderr, "Unique server ID: %s\n", _addr.uservid ?
244 _addr.uservid : "<any>");
245 fprintf(stderr, "Port: %s\n", _addr.port);
246 fprintf(stderr, "Path: %s\n", _addr.path);
247 fprintf(stderr, "Vhost header: %s\n",
248 _addr.vhost_header);
249 fprintf(stderr, "Transport protocol: %s\n",
250 _addr.transport_proto);
251 fprintf(stderr, "Address space: %s\n",
252 _addr.address_space ? _addr.address_space :
253 "<unspecified>");
254 fprintf(stderr, "\n");
257 return _addr;
260 #define MODE_ALLOW_EOF 0
261 #define MODE_HANDSHAKE 1
262 #define MAXFDS 128
264 static void disconnect_outgoing(struct user *user)
266 if(user_get_red_out(user))
267 user_set_red_io(user, -1, -1, -1);
268 else
269 user_set_red_io(user, -1, 1, -1);
270 user_send_red_in_eof(user);
273 static void traffic_loop(struct user *user, int mode)
275 fd_set rfds;
276 fd_set wfds;
277 struct pollfd polled[MAXFDS];
278 int fdcount = 0;
279 int failcode = 0;
280 struct timeval deadline;
281 int bound = 0;
282 int r;
283 FD_ZERO(&rfds);
284 FD_ZERO(&wfds);
285 user_add_to_sets(user, &bound, &rfds, &wfds, &deadline);
286 if (bound == 0) {
287 failcode = user_get_failure(user);
288 if (failcode)
289 goto failed;
290 return;
292 for(r = 0; r < bound || r <= socket_fd; r++) {
293 int added = 0;
294 polled[fdcount].fd = r;
295 polled[fdcount].events = 0;
296 polled[fdcount].revents = 0;
297 if(FD_ISSET(r, &rfds)) {
298 polled[fdcount].events |= (POLLIN | POLLHUP);
299 added = 1;
301 if(FD_ISSET(r, &wfds)) {
302 polled[fdcount].events |= (POLLOUT | POLLHUP);
303 added = 1;
305 if(r == socket_fd) {
306 polled[fdcount].events |= POLLHUP;
307 added = 1;
309 if(added)
310 fdcount++;
313 r = poll(polled, fdcount, -1);
314 if (r < 0 && errno != EINTR) {
315 die_errno("poll() failed");
316 } else if (r <= 0) {
317 FD_ZERO(&rfds);
318 FD_ZERO(&wfds);
319 } else {
320 FD_ZERO(&rfds);
321 FD_ZERO(&wfds);
322 for(r = 0; r < fdcount; r++) {
323 if(polled[r].revents & POLLHUP) {
324 /* Write stream hangup. Disconnect. */
325 if(polled[r].fd == 0)
326 FD_SET(0, &rfds);
327 if(polled[r].fd == 1)
328 FD_SET(1, &wfds);
329 if(polled[r].fd > 1)
330 disconnect_outgoing(user);
332 if(polled[r].revents & POLLIN)
333 FD_SET(polled[r].fd, &rfds);
334 if(polled[r].revents & POLLOUT)
335 FD_SET(polled[r].fd, &wfds);
338 in_handler = 1;
339 user_service(user, &rfds, &wfds);
340 in_handler = 0;
341 if (sigusr1_flag) {
342 sigusr1_flag = 0;
343 user_debug(debug_for, stderr);
345 failcode = user_get_failure(user);
346 if (failcode)
347 goto failed;
348 if (user_red_out_eofd(user) && !cbuffer_used(
349 user_get_red_out_force(user))) {
350 failcode = 1;
351 goto failed;
354 return;
355 failed:
356 if (failcode > 0 && mode == MODE_ALLOW_EOF)
357 return;
358 else if (failcode > 0) {
359 error("Expected more data, got connection closed");
360 fprintf(stderr, "Debug dump:\n");
361 user_debug(user, stderr);
362 die("Expected more data, got connection closed");
363 } else if (failcode < 0) {
364 const char *major;
365 const char *minor;
367 major = user_explain_failure(failcode);
368 minor = user_get_error(user);
370 if (minor)
371 die("Connection lost: %s (%s)", major, minor);
372 else
373 die("Connection lost: %s", major);
377 static int select_keypair(gnutls_certificate_credentials_t creds,
378 const char *username, int must_succeed)
380 int ret;
381 ret = select_keypair_int(creds, username);
382 if (ret < 0 && must_succeed)
383 die("No keypair identity %s found", username);
384 return ret;
387 static gnutls_session_t session;
389 static void preconfigure_tls(const char *username)
391 int s;
392 gnutls_certificate_credentials_t creds;
393 int keypair_ok = 0;
394 #ifndef DISABLE_SRP
395 const char *srp_password;
396 gnutls_srp_client_credentials_t srp_cred;
397 #endif
398 int kx[3];
400 s = gnutls_global_init();
401 if (s < 0)
402 die("Can't initialize GnuTLS: %s", gnutls_strerror(s));
404 s = gnutls_certificate_allocate_credentials(&creds);
405 if (s < 0)
406 die("Can't allocate cert creds: %s", gnutls_strerror(s));
408 s = gnutls_init(&session, GNUTLS_CLIENT);
409 if (s < 0)
410 die("Can't allocate session: %s", gnutls_strerror(s));
412 #ifndef DISABLE_SRP
413 s = gnutls_priority_set_direct (session, "NORMAL:+SRP-DSS:+SRP-RSA",
414 NULL);
415 #else
416 s = gnutls_priority_set_direct (session, "NORMAL", NULL);
417 #endif
418 if (s < 0)
419 die("Can't set priority: %s", gnutls_strerror(s));
421 if (username) {
422 if (!prefixcmp(username, "ssh-")) {
423 do_ssh_preauth(username + 4);
424 keypair_ok = 1;
425 goto skip_ksel;
427 if (!prefixcmp(username, "key-")) {
428 select_keypair(creds, username + 4, 1);
429 keypair_ok = 1;
430 } else
431 keypair_ok = (select_keypair(creds, username, 0)
432 >= 0);
435 skip_ksel:
436 s = gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, creds);
437 if (s < 0)
438 die("Can't set creds: %s", gnutls_strerror(s));
440 if (keypair_ok)
441 goto no_srp;
442 #ifndef DISABLE_SRP
443 if (username && !prefixcmp(username, "srp-"))
444 username = username + 4;
445 if (!username || !*username)
446 goto no_srp;
448 s = gnutls_srp_allocate_client_credentials(&srp_cred);
449 if (s < 0)
450 die("Can't allocate SRP creds: %s", gnutls_strerror(s));
452 s = 0;
453 srp_password = get_srp_password(username);
454 s = gnutls_srp_set_client_credentials(srp_cred, username, srp_password);
455 if (s < 0)
456 die("Can't set SRP creds: %s", gnutls_strerror(s));
458 s = gnutls_credentials_set(session, GNUTLS_CRD_SRP, srp_cred);
459 if (s < 0)
460 die("Can't use SRP creds: %s", gnutls_strerror(s));
462 /* GnuTLS doesn't seem to like to use SRP. Force it. */
463 kx[0] = GNUTLS_KX_SRP_DSS;
464 kx[1] = GNUTLS_KX_SRP_RSA;
465 kx[2] = 0;
466 s = gnutls_kx_set_priority(session, kx);
467 if (s < 0)
468 die("Can't force SRP: %s", gnutls_strerror(s));
469 #endif
470 goto skip_force;
471 no_srp:
472 kx[0] = GNUTLS_KX_DHE_DSS;
473 kx[1] = GNUTLS_KX_DHE_RSA;
474 kx[2] = 0;
475 s = gnutls_kx_set_priority(session, kx);
476 if (s < 0)
477 die("Can't force Diffie-Hellman: %s", gnutls_strerror(s));
478 skip_force:
482 static void configure_tls(struct user *user, const char *hostname)
484 user_configure_tls(user, session);
486 /* Wait for TLS connection to establish. */
487 while (!user_get_tls(user))
488 traffic_loop(user, MODE_HANDSHAKE);
490 if(hostname)
491 check_hostkey(session, hostname);
494 #define MAX_REQUEST 8192
495 const char *hexes = "0123456789abcdef";
497 static void do_request(const char *arg, struct parsed_addr *addr,
498 int suppress_ok, int no_repo)
500 int fd;
501 struct user *dispatcher;
502 struct cbuffer *inbuf;
503 struct cbuffer *outbuf;
504 const char *major;
505 const char *minor;
506 char reqbuf[MAX_REQUEST + 4];
507 size_t reqsize;
508 int do_tls = 0;
510 #ifdef SIGPIPE
511 signal(SIGPIPE, SIG_IGN);
512 #endif
514 preconfigure_tls(addr->user);
516 fd = connect_host(addr->host, addr->port,
517 addr->transport_proto, addr->address_space);
519 /* Create dispatcher with no time limit. */
520 debug_for = dispatcher = user_create(fd, 65535);
521 if (!dispatcher)
522 die("Can't create connection context");
523 socket_fd = fd;
524 user_clear_deadline(dispatcher);
526 inbuf = user_get_red_in(dispatcher);
527 outbuf = user_get_red_out(dispatcher);
528 if (!strcmp(addr->protocol, "git")) {
529 ; /* Not protected. */
530 } else if (!strcmp(addr->protocol, "tls")) {
531 if (verbose)
532 fprintf(stderr, "Configuring TLS...\n");
533 configure_tls(dispatcher, addr->uservid);
534 do_tls = 1;
535 } else {
536 if (verbose)
537 fprintf(stderr, "Requesting TLS...\n");
538 cbuffer_write(inbuf, (unsigned char*)"000cstarttls", 12);
539 while (1) {
540 char tmpbuf[9];
541 int s;
542 traffic_loop(dispatcher, MODE_HANDSHAKE);
543 s = cbuffer_peek(outbuf, (unsigned char*)tmpbuf, 8);
544 tmpbuf[8] = '\0';
545 if (s >= 0 && !strcmp(tmpbuf, "proceed\n"))
546 break;
547 if (s >= 0 && !strcmp(tmpbuf, "notsupp\n"))
548 die("Server does not support gits://");
549 if (user_red_out_eofd(dispatcher))
550 goto wait_eofd;
551 if (user_get_failure(dispatcher))
552 goto wait_failed;
554 if (verbose)
555 fprintf(stderr, "Server ready for TLS. Configuring TLS...\n");
556 configure_tls(dispatcher, addr->uservid);
557 do_tls = 1;
560 if (do_tls) {
561 if (verbose)
562 fprintf(stderr, "Waiting for TLS link to establish...\n");
564 while (!user_get_tls(dispatcher))
565 traffic_loop(dispatcher, MODE_HANDSHAKE);
567 if (verbose)
568 fprintf(stderr, "Secure link established.\n");
571 if (addr->user && !prefixcmp(addr->user, "ssh-")) {
572 if (verbose)
573 fprintf(stderr, "Sending SSH auth request...\n");
574 send_ssh_authentication(dispatcher, addr->user + 4);
575 while (cbuffer_used(inbuf))
576 traffic_loop(dispatcher, MODE_HANDSHAKE);
577 if (verbose)
578 fprintf(stderr, "Sent SSH auth request.\n");
581 if (!no_repo)
582 reqsize = strlen(arg) + strlen(addr->path) + 3 +
583 strlen(addr->vhost_header);
584 else
585 reqsize = strlen(arg);
587 if (reqsize > MAX_REQUEST)
588 die("Request too big to send");
590 memcpy(reqbuf + 4, arg, strlen(arg));
591 if (!no_repo) {
592 reqbuf[strlen(arg) + 4] = ' ';
593 memcpy(reqbuf + strlen(arg) + 5, addr->path, strlen(addr->path) + 1);
594 memcpy(reqbuf + strlen(arg) + 6 + strlen(addr->path),
595 addr->vhost_header, strlen(addr->vhost_header) + 1);
598 reqbuf[0] = hexes[((reqsize + 4) >> 12) & 0xF];
599 reqbuf[1] = hexes[((reqsize + 4) >> 8) & 0xF];
600 reqbuf[2] = hexes[((reqsize + 4) >> 4) & 0xF];
601 reqbuf[3] = hexes[(reqsize + 4) & 0xF];
603 if (verbose)
604 fprintf(stderr, "Sending request...\n");
605 cbuffer_write(inbuf, (unsigned char*)reqbuf, reqsize + 4);
606 if (verbose)
607 fprintf(stderr, "Request sent, waiting for reply...\n");
608 while (!cbuffer_used(outbuf) && !suppress_ok)
609 traffic_loop(dispatcher, MODE_HANDSHAKE);
610 if (verbose)
611 fprintf(stderr, "Server replied.\n");
612 /* Ok, remote end has replied. */
613 if(!suppress_ok)
614 printf("\n");
615 fflush(stdout);
616 user_set_red_io(dispatcher, 0, 1, -1);
618 while (!user_get_failure(dispatcher))
619 traffic_loop(dispatcher, MODE_ALLOW_EOF);
620 exit(0);
622 wait_failed:
623 major = user_explain_failure(user_get_failure(dispatcher));
624 minor = user_get_error(dispatcher);
626 if (minor)
627 die("Connection lost: %s (%s)", major, minor);
628 else
629 die("Connection lost: %s", major);
630 exit(128);
631 wait_eofd:
632 die("Expected response to starttls, server closed connection.");
633 exit(128);
636 int main(int argc, char **argv)
638 struct parsed_addr paddr;
639 char buffer[8192];
641 if (getenv("GITS_VERBOSE"))
642 verbose = 1;
644 if (argc < 3) {
645 die("Need two arguments");
648 signal(SIGUSR1, sigusr1_handler);
650 paddr = parse_address(argv[2]);
652 if (!prefixcmp(argv[1], "--service=")) {
653 do_request(argv[1] + 10, &paddr, 1, 0);
654 return 0;
656 if (!prefixcmp(argv[1], "--nourl-service=")) {
657 do_request(argv[1] + 16, &paddr, 1, 1);
658 return 0;
661 while (1) {
662 char *cmd;
664 cmd = fgets(buffer, 8190, stdin);
665 if (cmd[strlen(cmd) - 1] == '\n')
666 cmd[strlen(cmd) - 1] = '\0';
668 if (!strcmp(cmd, "capabilities")) {
669 printf("*connect\n\n");
670 fflush(stdout);
671 } else if (!*cmd) {
672 exit(0);
673 } else if (!prefixcmp(cmd, "connect ")) {
674 do_request(cmd + 8, &paddr, 0, 0);
675 return 0;
676 } else
677 die("Unknown command %s", cmd);
679 return 0;