Added PWMD_OPTION_SSH_KEEPALIVE.
[libpwmd.git] / src / ssh.c
blobfabe0c34b594fbb91a12728d29724b5850028dec
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011
4 Ben Kibbey <bjk@luxsci.net>
6 This file is part of libpwmd.
8 Libpwmd is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
13 Libpwmd is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with Libpwmd. If not, see <http://www.gnu.org/licenses/>.
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <err.h>
30 #include <pwd.h>
31 #include <netdb.h>
32 #include <netinet/in.h>
33 #include <sys/socket.h>
34 #include <arpa/inet.h>
35 #include <ctype.h>
36 #include <time.h>
37 #include <pthread.h>
38 #include <limits.h>
39 #include <libpwmd.h>
41 #ifndef LINE_MAX
42 #define LINE_MAX 2048
43 #endif
45 #include "types.h"
46 #include "misc.h"
47 #include "ssh.h"
49 static gpg_error_t ssh_connect_finalize(pwm_t *pwm);
50 static gpg_error_t _setup_ssh_init(pwm_t *pwm);
51 static gpg_error_t _setup_ssh_authlist(pwm_t *pwm);
52 static gpg_error_t _setup_ssh_auth(pwm_t *pwm);
53 static gpg_error_t _setup_ssh_channel(pwm_t *pwm);
54 static gpg_error_t _setup_ssh_shell(pwm_t *pwm);
55 static gpg_error_t _setup_ssh_agent(pwm_t *pwm);
57 static void close_agent(pwmd_tcp_conn_t *conn)
59 if (conn->agent) {
60 libssh2_agent_disconnect(conn->agent);
61 libssh2_agent_free(conn->agent);
62 conn->agent = NULL;
66 static void ssh_deinit(pwmd_tcp_conn_t *conn)
68 if (!conn)
69 return;
71 close_agent(conn);
72 /* Fixes error messages in the pwmd log. */
73 libssh2_channel_wait_closed(conn->channel);
75 if (conn->channel) {
76 libssh2_channel_close(conn->channel);
77 libssh2_channel_free(conn->channel);
80 if (conn->kh) {
81 libssh2_knownhost_free(conn->kh);
82 conn->kh = NULL;
85 if (conn->session) {
86 libssh2_session_disconnect(conn->session, N_("libpwmd saying bye!"));
87 libssh2_session_free(conn->session);
90 conn->session = NULL;
91 conn->channel = NULL;
92 _free_ssh_conn(conn);
95 ssize_t read_hook_ssh(pwm_t *pwm, assuan_fd_t fd, void *data, size_t len)
97 ssize_t ret = libssh2_channel_read(pwm->tcp_conn->channel, data, len);
99 if (ret == LIBSSH2_ERROR_TIMEOUT)
100 errno = ETIMEDOUT;
102 return ret;
105 ssize_t write_hook_ssh(pwm_t *pwm, assuan_fd_t fd, const void *data, size_t len)
107 ssize_t ret;
109 /* libassuan cannot handle EAGAIN when doing writes. */
110 do {
111 ret = libssh2_channel_write(pwm->tcp_conn->channel, data, len);
112 if (ret == LIBSSH2_ERROR_EAGAIN) {
113 usleep(50000);
115 } while (ret == LIBSSH2_ERROR_EAGAIN);
117 if (ret == LIBSSH2_ERROR_TIMEOUT)
118 errno = ETIMEDOUT;
120 return ret;
123 void _free_ssh_conn(pwmd_tcp_conn_t *conn)
125 if (!conn)
126 return;
128 if (conn->username) {
129 pwmd_free(conn->username);
130 conn->username = NULL;
133 if (conn->known_hosts) {
134 pwmd_free(conn->known_hosts);
135 conn->known_hosts = NULL;
138 if (conn->identity) {
139 pwmd_free(conn->identity);
140 conn->identity = NULL;
143 if (conn->identity_pub) {
144 pwmd_free(conn->identity_pub);
145 conn->identity_pub = NULL;
148 if (conn->host) {
149 pwmd_free(conn->host);
150 conn->host = NULL;
153 if (conn->hostkey) {
154 pwmd_free(conn->hostkey);
155 conn->hostkey = NULL;
158 if (!conn->session && conn->fd >= 0) {
159 close(conn->fd);
160 conn->fd = -1;
163 if (conn->addrs) {
164 freeaddrinfo(conn->addrs);
165 conn->addrs = NULL;
168 if (conn->session)
169 ssh_deinit(conn);
170 else
171 pwmd_free(conn);
174 static gpg_error_t init_tcp_conn(pwmd_tcp_conn_t **dst, const char *host,
175 int port, const char *identity, const char *user,
176 const char *known_hosts, int use_agent)
178 pwmd_tcp_conn_t *conn = *dst;
179 gpg_error_t rc = 0;
180 char *pwbuf = NULL;
181 struct passwd pw;
183 if (!host || !*host || (!use_agent && (!identity || !*identity))
184 || (known_hosts && !*known_hosts))
185 return GPG_ERR_INV_ARG;
187 conn = pwmd_calloc(1, sizeof(pwmd_tcp_conn_t));
188 if (!conn)
189 return gpg_error_from_errno(ENOMEM);
191 pwbuf = _getpwuid(&pw);
192 if (!pwbuf) {
193 rc = gpg_error_from_errno(errno);
194 goto fail;
197 if (conn->username)
198 pwmd_free(conn->username);
200 conn->username = pwmd_strdup(user ? user : pw.pw_name);
201 if (!conn->username) {
202 rc = gpg_error_from_errno(ENOMEM);
203 goto fail;
206 if (conn->identity)
207 pwmd_free(conn->identity);
209 conn->identity = NULL;
210 if (conn->identity_pub)
211 pwmd_free(conn->identity_pub);
213 conn->identity_pub = NULL;
215 if (identity) {
216 conn->identity = _expand_homedir((char *)identity, &pw);
218 if (!conn->identity) {
219 rc = gpg_error_from_errno(ENOMEM);
220 goto fail;
223 conn->identity_pub = pwmd_strdup_printf("%s.pub", conn->identity);
224 if (!conn->identity_pub) {
225 rc = gpg_error_from_errno(ENOMEM);
226 goto fail;
230 if (conn->known_hosts)
231 pwmd_free(conn->known_hosts);
233 if (!known_hosts)
234 known_hosts = "~/.ssh/known_hosts";
236 conn->known_hosts = _expand_homedir((char *)known_hosts, &pw);
237 if (!conn->known_hosts) {
238 rc = gpg_error_from_errno(ENOMEM);
239 goto fail;
242 pwmd_free(pwbuf);
243 conn->port = port;
244 conn->host = pwmd_strdup(host);
245 if (!conn->host) {
246 rc = gpg_error_from_errno(ENOMEM);
247 goto fail;
250 *dst = conn;
251 return 0;
253 fail:
254 if (pwbuf)
255 pwmd_free(pwbuf);
257 _free_ssh_conn(conn);
258 return rc;
261 static void *ssh_malloc(size_t size, void **data)
263 return pwmd_malloc(size);
266 static void ssh_free(void *ptr, void **data)
268 pwmd_free(ptr);
271 static void *ssh_realloc(void *ptr, size_t size, void **data)
273 return pwmd_realloc(ptr, size);
276 static gpg_error_t _setup_ssh_agent(pwm_t *pwm)
278 int n;
279 struct libssh2_agent_publickey *identity = NULL;
280 struct libssh2_agent_publickey *identity_prev = NULL;
282 n = libssh2_agent_connect(pwm->tcp_conn->agent);
284 if (n) {
285 _free_ssh_conn(pwm->tcp_conn);
286 pwm->tcp_conn = NULL;
287 return GPG_ERR_NO_AGENT;
290 n = libssh2_agent_list_identities(pwm->tcp_conn->agent);
292 if (n) {
293 _free_ssh_conn(pwm->tcp_conn);
294 pwm->tcp_conn = NULL;
295 return GPG_ERR_KEYRING_OPEN;
298 n = libssh2_agent_get_identity(pwm->tcp_conn->agent, &identity,
299 identity_prev);
301 if (n > 0) {
302 _free_ssh_conn(pwm->tcp_conn);
303 pwm->tcp_conn = NULL;
304 return GPG_ERR_NO_SECKEY;
306 else if (n < 0) {
307 _free_ssh_conn(pwm->tcp_conn);
308 pwm->tcp_conn = NULL;
309 return GPG_ERR_AGENT;
312 for (;;) {
313 do {
314 n = libssh2_agent_userauth(pwm->tcp_conn->agent,
315 pwm->tcp_conn->username, identity);
316 if (n == LIBSSH2_ERROR_EAGAIN)
317 usleep(50000);
318 } while (n == LIBSSH2_ERROR_EAGAIN);
320 if (!n)
321 break;
323 if (n && n != LIBSSH2_ERROR_AUTHENTICATION_FAILED) {
324 _free_ssh_conn(pwm->tcp_conn);
325 pwm->tcp_conn = NULL;
326 return GPG_ERR_ASS_SERVER_START;
329 identity_prev = identity;
330 n = libssh2_agent_get_identity(pwm->tcp_conn->agent, &identity,
331 identity_prev);
333 if (n > 0) {
334 _free_ssh_conn(pwm->tcp_conn);
335 pwm->tcp_conn = NULL;
336 return GPG_ERR_NO_SECKEY;
338 else if (n < 0) {
339 _free_ssh_conn(pwm->tcp_conn);
340 pwm->tcp_conn = NULL;
341 return GPG_ERR_AGENT;
345 return _setup_ssh_channel(pwm);
348 static gpg_error_t _setup_ssh_auth(pwm_t *pwm)
350 int n;
352 if (pwm->use_agent)
353 return _setup_ssh_agent(pwm);
355 do {
356 n = libssh2_userauth_publickey_fromfile(pwm->tcp_conn->session,
357 pwm->tcp_conn->username, pwm->tcp_conn->identity_pub,
358 pwm->tcp_conn->identity, NULL);
359 if (n == LIBSSH2_ERROR_EAGAIN)
360 usleep(50000);
361 } while (n == LIBSSH2_ERROR_EAGAIN);
363 if (n) {
364 _free_ssh_conn(pwm->tcp_conn);
365 pwm->tcp_conn = NULL;
367 switch (n) {
368 case LIBSSH2_ERROR_FILE:
369 return GPG_ERR_UNUSABLE_SECKEY;
370 case LIBSSH2_ERROR_TIMEOUT:
371 return GPG_ERR_TIMEOUT;
372 case LIBSSH2_ERROR_AUTHENTICATION_FAILED:
373 return GPG_ERR_BAD_SECKEY;
376 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_TIMEOUT : GPG_ERR_ASSUAN_SERVER_FAULT;
379 return _setup_ssh_channel(pwm);
382 static gpg_error_t _setup_ssh_authlist(pwm_t *pwm)
384 char *userauth;
385 int n;
387 do {
388 userauth = libssh2_userauth_list(pwm->tcp_conn->session,
389 pwm->tcp_conn->username, strlen(pwm->tcp_conn->username));
390 n = libssh2_session_last_errno(pwm->tcp_conn->session);
391 if (n == LIBSSH2_ERROR_EAGAIN)
392 usleep(50000);
393 } while (!userauth && n == LIBSSH2_ERROR_EAGAIN);
395 if (n)
396 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_TIMEOUT : GPG_ERR_ASSUAN_SERVER_FAULT;
398 if (!userauth) {
399 _free_ssh_conn(pwm->tcp_conn);
400 pwm->tcp_conn = NULL;
401 return GPG_ERR_BAD_PIN_METHOD;
404 if (!userauth || !strstr(userauth, "publickey")) {
405 _free_ssh_conn(pwm->tcp_conn);
406 pwm->tcp_conn = NULL;
407 return GPG_ERR_BAD_PIN_METHOD;
410 return _setup_ssh_auth(pwm);
413 static void add_knownhost(pwm_t *pwm, const char *host, const char *key,
414 size_t len, int type, struct libssh2_knownhost **dst)
416 char *buf;
418 if (pwm->tcp_conn->port != -1 && pwm->tcp_conn->port != 22) {
419 buf = pwmd_malloc(256);
420 snprintf(buf, 256, "[%s]:%i", host, pwm->tcp_conn->port);
422 else
423 buf = pwmd_strdup(host);
425 char *tbuf = pwmd_strdup_printf("libpwmd-%li", time(NULL));
426 libssh2_knownhost_addc(pwm->tcp_conn->kh, buf, NULL, key, len, tbuf,
427 strlen(tbuf), type, dst);
428 pwmd_free(tbuf);
429 pwmd_free(buf);
432 static gpg_error_t check_known_hosts(pwm_t *pwm)
434 size_t len;
435 int type;
436 const char *key;
437 gpg_error_t rc = 0;
438 int n;
439 struct libssh2_knownhost *kh;
441 key = libssh2_session_hostkey(pwm->tcp_conn->session, &len, &type);
443 while (!libssh2_knownhost_get(pwm->tcp_conn->kh, &kh, NULL))
444 libssh2_knownhost_del(pwm->tcp_conn->kh, kh);
446 n = libssh2_knownhost_readfile(pwm->tcp_conn->kh,
447 pwm->tcp_conn->known_hosts, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
449 if (n < 0 && n != LIBSSH2_ERROR_FILE)
450 return GPG_ERR_BAD_CERT;
452 n = libssh2_knownhost_checkp(pwm->tcp_conn->kh, pwm->tcp_conn->host,
453 pwm->tcp_conn->port, (char *)key, len,
454 LIBSSH2_KNOWNHOST_TYPE_PLAIN|LIBSSH2_KNOWNHOST_KEYENC_RAW,
455 &pwm->tcp_conn->hostent);
456 type = type == LIBSSH2_HOSTKEY_TYPE_RSA ?
457 LIBSSH2_KNOWNHOST_KEY_SSHRSA : LIBSSH2_KNOWNHOST_KEY_SSHDSS;
459 switch (n) {
460 case LIBSSH2_KNOWNHOST_CHECK_MATCH:
461 break;
462 case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
463 if (!pwm->kh_cb)
464 rc = GPG_ERR_NOT_CONFIRMED;
465 else
466 rc = pwm->kh_cb(pwm->kh_data, pwm->tcp_conn->host, key,
467 len);
468 if (rc)
469 return rc;
471 /* Adds both the IP and hostname. */
472 char *ip = pwmd_malloc(255);
474 if (ip) {
475 char *tmp;
476 int n = getnameinfo(pwm->tcp_conn->addr->ai_addr,
477 pwm->tcp_conn->addr->ai_family == AF_INET ?
478 sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6),
479 ip, 255, NULL, 0, NI_NUMERICHOST);
481 if (n) {
482 pwmd_free(ip);
483 return 0;
486 if (strcmp(pwm->tcp_conn->host, ip))
487 tmp = pwmd_strdup_printf("%s,%s", pwm->tcp_conn->host, ip);
488 else
489 tmp = pwmd_strdup(ip);
491 if (tmp)
492 add_knownhost(pwm, tmp, key, len,
493 LIBSSH2_KNOWNHOST_TYPE_PLAIN |
494 LIBSSH2_KNOWNHOST_KEYENC_RAW | type,
495 &pwm->tcp_conn->hostent);
497 pwmd_free(ip);
498 pwmd_free(tmp);
501 /* It's not an error if writing the new host file fails since
502 * there isn't a way to notify the user. The hostkey is still
503 * valid though. */
504 char *tmp = tempnam(NULL, "khost");
506 if (!tmp)
507 return 0;
509 if (!libssh2_knownhost_writefile(pwm->tcp_conn->kh, tmp,
510 LIBSSH2_KNOWNHOST_FILE_OPENSSH)) {
511 char *buf;
512 FILE *ifp, *ofp = NULL;
514 buf = pwmd_malloc(LINE_MAX);
515 if (!buf) {
516 unlink(tmp);
517 free(tmp);
518 return 0;
521 ifp = fopen(tmp, "r");
522 if (!ifp)
523 goto done;
525 ofp = fopen(pwm->tcp_conn->known_hosts, "w+");
526 if (!ofp)
527 goto done;
529 while ((fgets(buf, LINE_MAX, ifp))) {
530 if (fprintf(ofp, "%s", buf) < 0)
531 break;
534 done:
535 if (ifp)
536 fclose(ifp);
537 if (ofp)
538 fclose(ofp);
540 pwmd_free(buf);
543 unlink(tmp);
544 free(tmp);
545 return 0;
546 case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
547 case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
548 return GPG_ERR_BAD_CERT;
551 return 0;
554 static gpg_error_t verify_hostkey(pwm_t *pwm)
556 gpg_error_t rc;
557 size_t outlen;
558 char *buf;
560 if (!pwm->tcp_conn->kh)
561 pwm->tcp_conn->kh = libssh2_knownhost_init(pwm->tcp_conn->session);
562 if (!pwm->tcp_conn->kh)
563 return GPG_ERR_ENOMEM;
565 rc = check_known_hosts(pwm);
566 if (rc)
567 return rc;
569 buf = pwmd_malloc(LINE_MAX);
570 if (!buf)
571 return gpg_error_from_errno(ENOMEM);
573 if (libssh2_knownhost_writeline(pwm->tcp_conn->kh, pwm->tcp_conn->hostent,
574 buf, LINE_MAX, &outlen, LIBSSH2_KNOWNHOST_FILE_OPENSSH)) {
575 pwmd_free(buf);
576 return gpg_error_from_errno(ENOMEM);
579 if (pwm->tcp_conn->hostkey)
580 pwmd_free(pwm->tcp_conn->hostkey);
581 pwm->tcp_conn->hostkey = buf;
583 return _setup_ssh_authlist(pwm);
586 static gpg_error_t _setup_ssh_channel(pwm_t *pwm)
588 int n;
589 gpg_error_t rc = 0;
591 close_agent(pwm->tcp_conn);
593 do {
594 pwm->tcp_conn->channel =
595 libssh2_channel_open_session(pwm->tcp_conn->session);
597 n = libssh2_session_last_errno(pwm->tcp_conn->session);
598 if (n == LIBSSH2_ERROR_EAGAIN)
599 usleep(50000);
600 } while (!pwm->tcp_conn->channel && n == LIBSSH2_ERROR_EAGAIN);
602 if (!pwm->tcp_conn->channel || (n && n != LIBSSH2_ERROR_EAGAIN)) {
603 rc = GPG_ERR_ASS_SERVER_START;
604 _free_ssh_conn(pwm->tcp_conn);
605 pwm->tcp_conn = NULL;
606 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_TIMEOUT : rc;
609 return _setup_ssh_shell(pwm);
612 static gpg_error_t _setup_ssh_shell(pwm_t *pwm)
614 int n;
615 gpg_error_t rc;
617 do {
618 n = libssh2_channel_shell(pwm->tcp_conn->channel);
619 if (n == LIBSSH2_ERROR_EAGAIN)
620 usleep(50000);
621 } while (n == LIBSSH2_ERROR_EAGAIN);
623 if (n) {
624 rc = GPG_ERR_ASS_SERVER_START;
625 _free_ssh_conn(pwm->tcp_conn);
626 pwm->tcp_conn = NULL;
627 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_TIMEOUT : rc;
630 libssh2_keepalive_config(pwm->tcp_conn->session, 0, pwm->keepalive_interval);
631 return ssh_connect_finalize(pwm);
634 static gpg_error_t ssh_connect_finalize(pwm_t *pwm)
636 return assuan_socket_connect_fd(pwm->ctx, pwm->tcp_conn->fd, 0);
639 static gpg_error_t _setup_ssh_init(pwm_t *pwm)
641 int n;
643 do {
644 n = libssh2_session_handshake(pwm->tcp_conn->session,
645 pwm->tcp_conn->fd);
646 if (n == LIBSSH2_ERROR_EAGAIN)
647 usleep(50000);
648 } while (n == LIBSSH2_ERROR_EAGAIN);
650 if (n) {
651 _free_ssh_conn(pwm->tcp_conn);
652 pwm->tcp_conn = NULL;
653 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_TIMEOUT : GPG_ERR_ASSUAN_SERVER_FAULT;
656 return verify_hostkey(pwm);
659 gpg_error_t _setup_ssh_session(pwm_t *pwm)
661 gpg_error_t rc;
663 if (!pwm->tcp_conn->session) {
664 pwm->tcp_conn->session = libssh2_session_init_ex(ssh_malloc, ssh_free,
665 ssh_realloc, NULL);
666 if (!pwm->tcp_conn->session) {
667 rc = gpg_error_from_errno(ENOMEM);
668 goto fail;
671 libssh2_session_flag(pwm->tcp_conn->session, LIBSSH2_FLAG_COMPRESS, 1);
672 libssh2_session_set_timeout(pwm->tcp_conn->session,
673 pwm->ssh_timeout*1000);
675 if (pwm->use_agent)
676 pwm->tcp_conn->agent = libssh2_agent_init(pwm->tcp_conn->session);
679 if (!pwm->tcp_conn->session) {
680 rc = gpg_error_from_errno(ENOMEM);
681 goto fail;
684 libssh2_session_set_blocking(pwm->tcp_conn->session, 1);
685 return _setup_ssh_init(pwm);
687 fail:
688 _free_ssh_conn(pwm->tcp_conn);
689 pwm->tcp_conn = NULL;
690 return rc;
693 gpg_error_t _do_ssh_connect(pwm_t *pwm, const char *host, int port,
694 const char *identity, const char *user, const char *known_hosts)
696 pwmd_tcp_conn_t *conn = NULL;
697 gpg_error_t rc;
698 struct addrinfo hints = {0};
699 int n;
700 char portstr[6];
702 if (!pwm)
703 return GPG_ERR_INV_ARG;
705 rc = init_tcp_conn(&conn, host, port, identity, user, known_hosts,
706 pwm->use_agent);
707 if (rc)
708 return rc;
710 switch (pwm->prot) {
711 case PWMD_IP_ANY:
712 hints.ai_family = AF_UNSPEC;
713 break;
714 case PWMD_IPV4:
715 hints.ai_family = AF_INET;
716 break;
717 case PWMD_IPV6:
718 hints.ai_family = AF_INET6;
719 break;
722 pwm->tcp_conn = conn;
723 hints.ai_socktype = SOCK_STREAM;
724 snprintf(portstr, sizeof(portstr), "%i", port == -1 ? 22 : port);
725 n = getaddrinfo(pwm->tcp_conn->host, portstr, &hints, &pwm->tcp_conn->addrs);
726 if (n) {
727 fprintf(stderr, "%s\n", gai_strerror(n));
728 return GPG_ERR_UNKNOWN_HOST; //FIXME
731 for (pwm->tcp_conn->addr = pwm->tcp_conn->addrs; pwm->tcp_conn->addr;
732 pwm->tcp_conn->addr = pwm->tcp_conn->addrs->ai_next) {
733 pwm->tcp_conn->fd = socket(pwm->tcp_conn->addr->ai_family, SOCK_STREAM, 0);
734 if (pwm->tcp_conn->fd == -1) {
735 rc = gpg_error_from_syserror();
736 continue;
739 if (connect(pwm->tcp_conn->fd, pwm->tcp_conn->addr->ai_addr,
740 sizeof(struct sockaddr)) == -1) {
741 rc = gpg_error_from_syserror();
742 close(pwm->tcp_conn->fd);
743 pwm->tcp_conn->fd = -1;
744 continue;
747 rc = 0;
748 break;
751 if (rc)
752 return rc;
754 rc = _setup_ssh_session(pwm);
755 return rc;
759 * ssh[46]://[username@]hostname[:port]
761 * Any missing parameters are checked for in init_tcp_conn().
763 gpg_error_t _parse_ssh_url(char *str, char **host, int *port, char **user)
765 char *p;
766 char *t;
767 int len;
769 *host = *user = NULL;
770 *port = -1;
771 p = strrchr(str, '@');
772 if (p) {
773 len = strlen(str)-strlen(p)+1;
774 *user = pwmd_malloc(len);
775 if (!*user)
776 return gpg_error_from_errno(ENOMEM);
778 snprintf(*user, len, "%s", str);
779 p++;
781 else
782 p = str;
784 t = strchr(p, ':');
785 if (t) {
786 len = strlen(p)-strlen(t)+1;
787 *host = pwmd_malloc(len);
788 if (!*host)
789 return gpg_error_from_errno(ENOMEM);
791 snprintf(*host, len, "%s", p);
792 t++;
793 *port = atoi(t);
794 if (*t == '-')
795 t++;
797 while (*t && isdigit(*t))
798 t++;
800 p = t;
803 t = strchr(p, ',');
804 if (t) {
805 if (!*host) {
806 len = strlen(p)-strlen(t)+1;
807 *host = pwmd_malloc(len);
808 if (!*host)
809 return gpg_error_from_errno(ENOMEM);
811 snprintf(*host, len, "%s", p);
814 else {
815 if (!*host) {
816 len = strlen(p)+1;
817 *host = pwmd_malloc(len);
818 if (!*host)
819 return gpg_error_from_errno(ENOMEM);
821 snprintf(*host, len, "%s", p);
825 return 0;
828 void _ssh_disconnect(pwm_t *pwm)
830 ssh_deinit(pwm->tcp_conn);
831 pwm->tcp_conn = NULL;