├── .gitignore ├── LICENCE ├── Makefile ├── README.md ├── TODO ├── VERSION ├── charade.c ├── cmdline.c ├── cmdline.h ├── contrib ├── charade-0.0.2-1.cygport └── charade-0.0.2-1.src.patch ├── copyright.c ├── copyright.h ├── depend.sh ├── eprintf.c ├── eprintf.h ├── generate-copyright.pl ├── install.sh ├── pageant.c ├── pageant.h └── todo.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.d 2 | *.o 3 | charade.exe 4 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Charade is Copyright (c) 2009 Wesley Darlington. 2 | 3 | Portions are: 4 | Copyright 1997-2007 Simon Tatham. 5 | Copyright (c) 1995 Tatu Ylonen , Espoo, Finland. All Rights Reserved. 6 | Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions 10 | are met: 11 | 1. Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | 2. Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in the 15 | documentation and/or other materials provided with the distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export CC := gcc 2 | export CFLAGS := -Wall -Werror -O2 3 | 4 | all: charade.exe 5 | 6 | # sort to remove duplicates, specifically copyright.c 7 | # (it's auto-generated, but it'll appeare twice on second & subsequent makes: bad) 8 | CFILES := $(sort $(wildcard *.c) copyright.c) 9 | #OBJFILES := $(patsubst %.c,%.o,$(CFILES)) 10 | OBJFILES := $(CFILES:.c=.o) 11 | 12 | include $(OBJFILES:.o=.d) 13 | 14 | %d: %c 15 | ./depend.sh . $< > $@ 16 | 17 | install: all 18 | ./install.sh 19 | 20 | charade.exe: $(OBJFILES) 21 | $(CC) $(CFLAGS) -Wl,--enable-auto-import -o $@ $+ 22 | 23 | copyright.c: LICENCE generate-copyright.pl 24 | ./generate-copyright.pl LICENCE > $@ 25 | 26 | binary: all VERSION 27 | export VNAME=charade-`cat VERSION`; \ 28 | export DIR=temp/$$VNAME; \ 29 | mkdir -p $$DIR/ \ 30 | && cp charade.exe $$DIR \ 31 | && cp install.sh $$DIR \ 32 | && cp README.md $$DIR \ 33 | && (cd temp; tar cf - $$VNAME | bzip2 -9) > $$VNAME.tar.bz2 \ 34 | && echo created $$VNAME.tar.bz2 35 | 36 | clean: 37 | rm -f charade.exe 38 | rm -f *.o *.obj 39 | rm -f *.d 40 | rm -rf temp 41 | rm -f charade-*.tar.bz2 charade-*.tar.gz 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Intro 2 | ----- 3 | **Charade** is an ssh-agent in [cygwin](http://www.cygwin.com/) that proxies requests to [pageant](http://the.earth.li/~sgtatham/putty/0.60/htmldoc/Chapter9.html#pageant). 4 | 5 | If you don't use cygwin or pageant, you don't need charade. If you're happy with plink, you don't need charade. (I love putty, but I prefer openssh to plink.) 6 | 7 | **Background:** I tolerate Windows XP. I actually quite like it. But it needs putty, and it needs cygwin to be usable. I used to maintain two separate sets of ssh keys: one for putty, which I kept in pageant, and another for cygwin's openssh, which I kept in ssh-agent/keychain. 8 | 9 | Eventually I got fed up maintaining two key pairs and charade was born. 10 | 11 | Charade just pretends to be ssh-agent on one side and putty on the other. It's a shim between openssh and pageant. 12 | 13 | It aspires to be a drop-in replacement for ssh-agent, and that's how I use it atm. It works for me. It probably won't work for you. 14 | 15 | I stole large swaths of code from (i) Simon Tatham's putty (mostly [putty-src/windows/winpgntc.c](http://svn.tartarus.org/sgt/putty/)) and (ii) openssh's (and Tatu Ylonen's and Marcus Friedl's) ssh-agent.c. 16 | 17 | Also check out [cuviper's ssh-pageant](http://github.com/cuviper/ssh-pageant), which does something similar. 18 | 19 | Russell Davis has a clever way of [setting environment variables at boot](https://russelldavis.blogspot.com/2011/02/using-charade-to-proxy-cygwin-ssh-agent.html) so that processes launched outside your shells can also use your ssh keys. 20 | 21 | Required cygwin packages 22 | ------------------------ 23 | * `gcc-g++` 24 | * `perl` 25 | * `make` 26 | * `keychain` 27 | * `openssh` 28 | * `psmisc` 29 | 30 | Installation instructions 31 | ------------------------- 32 | 33 | 1. Clone the repository 34 | 35 | git clone git://github.com/wesleyd/charade.git 36 | 37 | 2. Build and install charade 38 | 39 | make && make install 40 | 41 | 3. Stop running charade or ssh-agent processes 42 | 43 | killall charade # Only if upgrading 44 | 45 | killall ssh-agent # Only if not upgrading 46 | 47 | 4. Remove existing keychain files to clear references to ssh-agent 48 | 49 | rm -rf ~/.keychain 50 | 51 | 52 | 5. Add these two lines to ~/.bash_profile: 53 | 54 | keychain -q -Q 55 | 56 | . ~/.keychain/`hostname`-sh 57 | 58 | 6. Logout, launch a new shell or putty and try it out 59 | 60 | ssh git@github.com 61 | 62 | If you have a key in pageant, you should be able to go where it points. 63 | 64 | It just forwards ssh messages, so surprising (to me!) things Just Work, like adding keys with ssh-add instead of through the pageant gui. 65 | 66 | Wesley Darlington, December 2009. 67 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | o Copyright stuff 2 | o Work on debug output 3 | o Check open files in each process that results 4 | o Signal handling 5 | o Specified agent socket 6 | o Redirect stderr (& stdout?) to *not* log if told not to 7 | o Do we remove ssh directory when finished??? (Signals!) 8 | o Check 64-bit cygwin 9 | o Package sanely for cygwin 10 | o Doesn't seem to work over RDP, but putty can speak to pageant fine. Hmmm. 11 | Is that the same as the plink bug? 12 | o Should charade die when subprocess dies? 13 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.0.2 2 | -------------------------------------------------------------------------------- /charade.c: -------------------------------------------------------------------------------- 1 | /* charade.c 2 | * 3 | * ssh-agent clone for cygwin to proxy all ssh-ish requests to pageant. 4 | * 5 | * Copyright (c) 2009, Wesley Darlington. All Rights Reserved. 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "cmdline.h" 24 | #include "eprintf.h" 25 | #include "pageant.h" 26 | #include "todo.h" 27 | 28 | #define LISTEN_BACKLOG 5 29 | #define BUFSIZE 8192 30 | 31 | // I picked this number out of my ass. It's, like, a sanity check. 32 | #define CRAZY_MAX_MESSAGE_SIZE 65535 33 | 34 | // TODO: is there actually something magical about this number? 35 | #define AGENT_COPYDATA_ID 0x804e50ba 36 | 37 | #define SSH_AUTHSOCKET_ENV_NAME "SSH_AUTH_SOCK" 38 | #define SSH_AGENTPID_ENV_NAME "SSH_AGENT_PID" 39 | 40 | typedef unsigned char byte; 41 | 42 | int listen_sock; 43 | struct socklist_node_t { 44 | TAILQ_ENTRY(socklist_node_t) next; 45 | int fd; 46 | byte *data; 47 | size_t len; 48 | }; 49 | TAILQ_HEAD(socklist_queue, socklist_node_t) socklist; 50 | 51 | char socket_dir[MAXPATHLEN] = ""; 52 | char socket_name[MAXPATHLEN] = ""; 53 | 54 | int remove_socket_at_exit = 0; 55 | 56 | void 57 | init_socket_list(void) 58 | { 59 | TAILQ_INIT(&socklist); 60 | } 61 | 62 | void 63 | add_socket_to_socket_list(int sock) 64 | { 65 | struct socklist_node_t *p = calloc(sizeof(struct socklist_node_t), 1); 66 | 67 | p->fd = sock; 68 | p->data = NULL; 69 | p->len = 0; 70 | 71 | EPRINTF(5, "adding socket %d to socket list.\n", sock); 72 | 73 | TAILQ_INSERT_TAIL(&socklist, p, next); 74 | } 75 | 76 | int 77 | num_sockets_in_list(void) 78 | { 79 | struct socklist_node_t *p; 80 | int count = 0; 81 | 82 | for (p = socklist.tqh_first; p != NULL; p = p->next.tqe_next) { 83 | ++count; 84 | } 85 | 86 | EPRINTF(5, "num sockets in list: %d.\n", count); 87 | 88 | return count; 89 | } 90 | 91 | void 92 | remove_socket_dir(void) /* atexit handler */ 93 | { 94 | if (remove_socket_at_exit) { 95 | int ret = rmdir(socket_dir); 96 | 97 | if (ret) { 98 | EPRINTF(0, "error removing socket dir '%s': %s.\n", 99 | socket_dir, strerror(errno)); 100 | /* atexit! */ 101 | } 102 | } 103 | } 104 | 105 | void 106 | remove_socket(void) /* atexit handler */ 107 | { 108 | if (remove_socket_at_exit) { 109 | int ret = unlink(socket_name); 110 | 111 | if (ret) { 112 | EPRINTF(0, "error removing socket '%s': %s.\n", 113 | socket_name, strerror(errno)); 114 | /* atexit! */ 115 | } 116 | } 117 | } 118 | 119 | 120 | void 121 | create_socket(void) 122 | { 123 | if (atexit(remove_socket_dir)) { 124 | EPRINTF(0, "Can't install atexit handler to delete socket dir.\n"); 125 | exit(1); 126 | } 127 | 128 | /* Create private directory for agent socket */ 129 | strlcpy(socket_dir, "/tmp/ssh-XXXXXXXXXX", sizeof socket_dir); 130 | if (mkdtemp(socket_dir) == NULL) { 131 | EPRINTF(0, "Can't mkdir socket directory: %s\n", 132 | strerror(errno)); 133 | exit(1); 134 | } 135 | 136 | remove_socket_at_exit = 1; 137 | 138 | int ret; 139 | if (g_socket_name) { 140 | ret = snprintf(socket_name, sizeof(socket_name), 141 | "%s", g_socket_name); 142 | } else { 143 | ret = snprintf(socket_name, sizeof(socket_name), 144 | "%s/agent.%ld", socket_dir, (long)getpid()); 145 | } 146 | if (ret >= sizeof(socket_name)) { 147 | // Would have liked to print more... 148 | EPRINTF(0, "socket_name too long (%d >= %zu).\n", 149 | ret, sizeof(socket_name)); 150 | exit(1); 151 | } 152 | 153 | listen_sock = socket(AF_UNIX, SOCK_STREAM, 0); 154 | if (listen_sock < 0) { 155 | EPRINTF(0, "socket error: %s.\n", strerror(errno)); 156 | exit(1); 157 | } 158 | 159 | struct sockaddr_un sunaddr; 160 | memset(&sunaddr, 0, sizeof(sunaddr)); 161 | sunaddr.sun_family = AF_UNIX; 162 | strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path)); 163 | int prev_mask = umask(0177); 164 | if (bind(listen_sock, (struct sockaddr *) &sunaddr, sizeof(sunaddr)) < 0) { 165 | EPRINTF(0, "bind() error: %s.\n", strerror(errno)); 166 | umask(prev_mask); 167 | exit(1); 168 | } 169 | 170 | if (atexit(remove_socket)) { 171 | EPRINTF(0, "Can't install atexit handler to delete socket '%s'; " 172 | "do it yourself!\n", socket_name); 173 | exit(1); 174 | } 175 | 176 | umask(prev_mask); 177 | if (listen(listen_sock, LISTEN_BACKLOG) < 0) { 178 | EPRINTF(0, "listen() error: %s", strerror(errno)); 179 | exit(1); 180 | } 181 | } 182 | 183 | void 184 | kill_old_agent(void) 185 | { 186 | char *pidstr = getenv(SSH_AGENTPID_ENV_NAME); 187 | if (pidstr == NULL) { 188 | EPRINTF(0, "%s not set, can't kill agent.\n", SSH_AGENTPID_ENV_NAME); 189 | exit(1); 190 | } 191 | 192 | const char *errstr = NULL; 193 | pid_t pid = strtol(pidstr, NULL, 10); // base 10 194 | if (errstr) { 195 | EPRINTF(0, "Invalid PID %s=\"%s\".\n", SSH_AGENTPID_ENV_NAME, pidstr); 196 | exit(1); 197 | } 198 | 199 | if (kill(pid, SIGTERM) == -1) { 200 | EPRINTF(0, "error from kill -TERM %ld: %s.\n", (long) pid, strerror(errno)); 201 | exit(1); 202 | } 203 | 204 | const char *format = g_csh_flag 205 | ? "unsetenv %s;\n" 206 | : "unset %s;\n"; 207 | printf(format, SSH_AUTHSOCKET_ENV_NAME); 208 | printf(format, SSH_AGENTPID_ENV_NAME); 209 | printf("echo Agent pid %ld killed;\n", (long)pid); 210 | exit(0); 211 | } 212 | 213 | void 214 | print_env_var(char *key, char *value) 215 | { 216 | if (g_csh_flag) { 217 | printf("setenv %s %s;\n", key, value); 218 | } else { 219 | printf("%s=%s; export %s\n", key, value, key); 220 | } 221 | } 222 | 223 | #define ITOA_BUFSIZE 64 224 | char * 225 | itoa_unsafe(int i) 226 | { 227 | static char buf[ITOA_BUFSIZE]; // Unsafeness number one 228 | if (snprintf(buf, sizeof(buf), "%d", i) > sizeof(buf)) { 229 | // Silently ignore: unsafeness number two 230 | } 231 | return buf; 232 | } 233 | #undef ITOA_BUFSIZE 234 | 235 | void 236 | print_env_stuff(int pid) 237 | { 238 | print_env_var(SSH_AUTHSOCKET_ENV_NAME, socket_name); 239 | print_env_var(SSH_AGENTPID_ENV_NAME, itoa_unsafe(pid)); 240 | } 241 | 242 | int 243 | make_poll_fds(struct pollfd **fds) 244 | { 245 | int nfds = 1 // The listening socket... 246 | + num_sockets_in_list(); // ...plus all the others. 247 | 248 | struct pollfd *pollfds = calloc(sizeof(struct pollfd), nfds); 249 | 250 | if (!pollfds) { 251 | EPRINTF(0, "Can't calloc() %d struct pollfd's for poll().\n", nfds); 252 | exit(1); 253 | } 254 | 255 | pollfds[0].fd = listen_sock; 256 | pollfds[0].events = POLLIN | POLLHUP; 257 | 258 | struct socklist_node_t *p; 259 | int i = 1; 260 | for (p = socklist.tqh_first; p != NULL; p = p->next.tqe_next) { 261 | 262 | if (i >= nfds) { 263 | EPRINTF(0, "Internal error: socket list changed beneath us.\n"); 264 | exit(1); 265 | } 266 | 267 | // TODO: Do we want POLLOUT and the close notifications too??? 268 | 269 | pollfds[i].events = POLLIN | POLLHUP; 270 | pollfds[i].fd = p->fd; 271 | ++i; 272 | } 273 | 274 | *fds = pollfds; 275 | return nfds; 276 | } 277 | 278 | void 279 | free_poll_fds(struct pollfd *fds) 280 | { 281 | free(fds); 282 | } 283 | 284 | void 285 | set_nonblock(int sock) 286 | { 287 | int flags = fcntl(sock, F_GETFL, 0); 288 | if (flags < 0) { 289 | EPRINTF(0, "Can't fcntl(%d, F_GETFL, 0): %s.\n", sock, strerror(errno)); 290 | exit(1); 291 | } 292 | 293 | flags |= O_NONBLOCK; 294 | 295 | if (fcntl(sock, F_SETFL, flags) == -1) { 296 | EPRINTF(0, "fcntl(%d, F_SETFL, O_NONBLOCK): %s.\n", sock, strerror(errno)); 297 | exit(1); 298 | } 299 | } 300 | 301 | void 302 | accept_new_socket(void) 303 | { 304 | struct sockaddr_un sunaddr; 305 | socklen_t socksize = sizeof(sunaddr); 306 | int newsock = accept(listen_sock, (struct sockaddr *) &sunaddr, &socksize); 307 | 308 | if (-1 == newsock) { 309 | EPRINTF(0, "accept error: %s", strerror(errno)); 310 | exit(1); 311 | } 312 | 313 | set_nonblock(newsock); 314 | 315 | add_socket_to_socket_list(newsock); 316 | } 317 | 318 | void 319 | fd_is_closed(int fd) 320 | { 321 | EPRINTF(3, "Removing fd %d from list.\n", fd); 322 | 323 | struct socklist_node_t *p; 324 | for (p = socklist.tqh_first; p != NULL; p = p->next.tqe_next) { 325 | if (p->fd == fd) { 326 | 327 | free(p->data); 328 | p->data = NULL; 329 | p->len = 0; 330 | 331 | TAILQ_REMOVE(&socklist, p, next); 332 | break; 333 | } 334 | } 335 | 336 | // ...and close it I guess... 337 | close(fd); 338 | } 339 | 340 | 341 | /* 342 | ssize_t numbytes = read(fds[i].fd, buf, count); 343 | 344 | if (0 == numbytes) { 345 | close(fds[i].fd); 346 | fd_is_closed(fds[i].fd); 347 | continue; 348 | } else if (-1 == numbytes) { 349 | // TODO: Should we handle EAGAIN/EWOULDBLOCK specially? 350 | EPRINTF(0, "internal error: read(fd=%d) => errno=%d/%s.\n", 351 | fds[i].fd, errno, strerror(errno)); 352 | close(fds[i].fd); 353 | fd_is_closed(fds[i].fd); 354 | continue; 355 | */ 356 | 357 | struct socklist_node_t * 358 | socklist_node_from_fd(int fd) 359 | { 360 | struct socklist_node_t *p; 361 | for (p = socklist.tqh_first; p != NULL; p = p->next.tqe_next) { 362 | if (p->fd == fd) { 363 | return p; 364 | } 365 | } 366 | return NULL; 367 | } 368 | 369 | // Return true if the socket - based on the data we've read from 370 | // it so far - can never, ever contain a valid message. 371 | int 372 | socket_will_never_contain_message(struct socklist_node_t *np) 373 | { 374 | if (np->len > CRAZY_MAX_MESSAGE_SIZE) { // OK, waaaay too much data 375 | return 1; 376 | } 377 | 378 | if (np->len < 4) { // Too soon to tell 379 | return 0; 380 | } 381 | 382 | unsigned int claimed_numbytes = GET_32BIT(np->data); 383 | if (claimed_numbytes > CRAZY_MAX_MESSAGE_SIZE) { 384 | return 1; 385 | } 386 | 387 | return 0; 388 | } 389 | 390 | 391 | // Read data from np->fd into np->buf, expanding np->buf as necessary. 392 | // Return zero for success. 393 | // Non-zero means some sort of failure, and caller should close socket etc. 394 | int 395 | read_data_for_node(struct socklist_node_t *np) 396 | { 397 | if (!np) { 398 | EPRINTF(0, "null pointer!"); 399 | return -1; 400 | } 401 | 402 | int data_still_to_be_read = 1; 403 | 404 | // byte buf[BUFSIZE]; 405 | byte buf[3]; 406 | 407 | while (data_still_to_be_read) { 408 | // Protect against infinite memory consumption... 409 | if(socket_will_never_contain_message(np)) { 410 | EPRINTF(0, "socket %d will never contain message.\n", np->fd); 411 | return -1; 412 | } 413 | 414 | ssize_t numbytes = read(np->fd, buf, sizeof buf); 415 | 416 | if (0 == numbytes) { 417 | EPRINTF(1, "read zero bytes from socket. Must have closed.\n"); 418 | return -1; 419 | } else if (-1 == numbytes) { 420 | if (EAGAIN == errno || EWOULDBLOCK == errno) { 421 | EPRINTF(5, "read(fd=%d) => EAGAIN/EWOULDBLOCK: errno=%d.\n", 422 | np->fd, errno); 423 | // No problem, this just means we've exhausted the socket... 424 | return 0; 425 | } else if (EINTR == errno) { 426 | EPRINTF(0, "read(fd=%d) => EINTR: retrying.\n", np->fd); 427 | continue; 428 | } else { 429 | EPRINTF(0, "internal error: read(fd=%d) => -1/errno=%d: %s.\n", 430 | np->fd, errno, strerror(errno)); 431 | return -1; 432 | } 433 | } else if (numbytes < 0) { 434 | EPRINTF(0, "weird!!! read(fd=%d) returned %d. Giving up.\n", 435 | np->fd, (int) numbytes); 436 | return -1; 437 | } else { 438 | // Normal case: there was data to be read, and we read it... 439 | // numbytes is positive (and not zero either!) 440 | 441 | if (numbytes < sizeof(buf)) { 442 | data_still_to_be_read = 0; 443 | } else if (numbytes > sizeof(buf)) { 444 | EPRINTF(0, "Aiee! Got more bytes than buffer! (%zu>%zu)\n", 445 | numbytes, sizeof(buf)); 446 | return -1; 447 | } else { // numbytes == sizeof(buf) 448 | data_still_to_be_read = 1; 449 | } 450 | 451 | // Right, now put the data in a sensible place... 452 | np->data = realloc(np->data, np->len + numbytes); 453 | if (!np->data) { 454 | EPRINTF(0, "realloc failure (%lu -> %lu)!\n", 455 | (long unsigned) np->len, 456 | (long unsigned) (np->len + numbytes)); 457 | return -1; 458 | } 459 | memcpy(np->data + np->len, buf, numbytes); 460 | np->len += numbytes; 461 | } 462 | } 463 | 464 | return 0; 465 | } 466 | 467 | int 468 | socket_contains_full_message(struct socklist_node_t *np) 469 | { 470 | if (np->len < 4) { 471 | return 0; 472 | } 473 | 474 | // If np->len is non-zero, then np->data is non-null... 475 | unsigned int claimed_numbytes = GET_32BIT(np->data); 476 | 477 | // The message length in the message doesn't include the four 478 | // bytes of the length itself. 479 | if (np->len < claimed_numbytes + 4) { 480 | EPRINTF(4, "short read? first four bytes imply %d bytes (%d+4=%d), " 481 | " but we only have %zu bytes.\n", 482 | claimed_numbytes+4, claimed_numbytes, 483 | claimed_numbytes+4, np->len); 484 | return 0; 485 | } else { 486 | EPRINTF(5, "claimed_numbytes is %u, but np->len is %lu.\n", 487 | claimed_numbytes, (long unsigned) np->len); 488 | return 1; 489 | } 490 | } 491 | 492 | void 493 | deal_with_ready_fds(struct pollfd *fds, int nfds) 494 | { 495 | EPRINTF(3, "we have %d fds in total\n", nfds); 496 | 497 | int i; 498 | for (i = 0; i < nfds; ++i) { 499 | const int fd = fds[i].fd; 500 | const short revents = fds[i].revents; 501 | 502 | EPRINTF(5, "What about fd %d (revents = 0x%hx)?\n", fd, revents); 503 | 504 | if (!revents) 505 | continue; 506 | 507 | if (listen_sock == fd) { 508 | // We daren't just *assume* that entry 0 is listen_sock... 509 | accept_new_socket(); 510 | continue; 511 | } else if (revents & POLLHUP) { // Does this ever even happen? 512 | close(fd); // Probably unnecessary, but harmless. (?) 513 | fd_is_closed(fd); 514 | continue; 515 | } else if (revents & POLLIN) { 516 | struct socklist_node_t *np = socklist_node_from_fd(fd); 517 | 518 | if (!np) { 519 | EPRINTF(0, "no such get_socklist_node for fd %d!\n", fd); 520 | continue; 521 | } 522 | 523 | if (read_data_for_node(np)) { 524 | // This includes if the socket has just closed... 525 | EPRINTF(2, "Error reading data for fd %d. Closing.\n", fd); 526 | close(fd); 527 | fd_is_closed(fd); 528 | continue; 529 | } 530 | 531 | if (socket_will_never_contain_message(np)) { 532 | EPRINTF(0, "Giving up on fd %d.\n", fd); 533 | close(fd); 534 | fd_is_closed(fd); 535 | continue; 536 | } 537 | 538 | if (socket_contains_full_message(np)) { 539 | EPRINTF(5, "Socket %d contains (at least) a full message.\n", 540 | np->fd); 541 | 542 | byte outbuf[BUFSIZE]; 543 | 544 | // Note, we *assume* that there won't be *more* than one 545 | // message in np... 546 | 547 | int retlen = send_request_to_pageant(np->data, np->len, 548 | outbuf, sizeof outbuf); 549 | if (retlen <= 0) { 550 | // Error sending message to pageant... 551 | EPRINTF(0, "Error sending %zu-byte message to pageant.\n", 552 | np->len); 553 | close(fd); 554 | fd_is_closed(fd); 555 | continue; 556 | } 557 | 558 | free(np->data); 559 | np->data = NULL; 560 | np->len = 0; 561 | 562 | // Now, send buf back to the socket. We should probably 563 | // loop and retry or use poll properly since it's nonblocking... 564 | ssize_t byteswritten = write(fd, outbuf, retlen); 565 | if (byteswritten != retlen) { 566 | EPRINTF(0, "Tried to write %d bytes back to ssh client, " 567 | "ended up writing %zu (errno is %d).\n", 568 | retlen, byteswritten, errno); 569 | close(fd); 570 | fd_is_closed(fd); 571 | } 572 | } else { 573 | EPRINTF(5, "Socket %d has %zu bytes, but not a full message.\n", 574 | np->fd, np->len); 575 | } 576 | } else { 577 | EPRINTF(0, "Don't know how to deal with revents=0x%x on fd %d.\n", 578 | revents, fd); 579 | close(fd); 580 | fd_is_closed(fd); 581 | } 582 | } 583 | } 584 | 585 | void 586 | handle_key_requests_forever(void) 587 | { 588 | EPRINTF(5, "entry.\n"); 589 | 590 | for (;;) { 591 | struct pollfd *fds; 592 | int nfds = make_poll_fds(&fds); 593 | 594 | EPRINTF(3, "Entering poll with %d fds.\n", nfds); 595 | int numready = poll(fds, nfds, -1); 596 | EPRINTF(3, "poll() returned %d ready.\n", numready); 597 | 598 | if (numready > 0) { 599 | deal_with_ready_fds(fds, nfds); 600 | } else if (numready < 0) { 601 | if (EINTR == errno) { 602 | EPRINTF(3, "poll() was EINTR-ed.\n"); 603 | continue; 604 | } else { 605 | EPRINTF(0, "poll error: %s.\n", strerror(errno)); 606 | exit(1); 607 | } 608 | } else if (0 == numready) { 609 | EPRINTF(0, "Internal error: poll() => 0, but no timeout was set.\n"); 610 | exit(1); 611 | } 612 | 613 | free_poll_fds(fds); 614 | } 615 | } 616 | 617 | void 618 | redirect(FILE *f, char *basename) 619 | { 620 | char buf[MAXPATHLEN] = ""; 621 | 622 | int ret = snprintf(buf, sizeof(buf), "%s/%s.%ld", 623 | socket_dir, basename, (long)getpid()); 624 | if (ret >= sizeof(buf)) { 625 | EPRINTF(0, "Can't open %s to redirect %s: Too long (%d >= %zu).\n", 626 | buf, basename, ret, sizeof(buf)); 627 | } else { 628 | EPRINTF(1, "Redirecting %s to %s.\n", basename, buf); 629 | 630 | if (!freopen(buf, "w", f)) { 631 | EPRINTF(0, "can't freopen %s.\n", basename); 632 | } 633 | 634 | // Turn off buffering... 635 | setvbuf(f, NULL, _IONBF, 0); 636 | 637 | EPRINTF(1, "Redirected %s to %s.\n", basename, buf); 638 | } 639 | } 640 | 641 | void 642 | redirect_stdall(void) 643 | { 644 | fclose(stdin); 645 | 646 | /* 647 | if (get_loudness() <= 0) { 648 | fclose(stdout); 649 | fclose(stderr); 650 | } else { 651 | redirect(stdout, "stdout"); 652 | redirect(stderr, "stderr"); 653 | } 654 | */ 655 | 656 | redirect(stdout, "stdout"); 657 | redirect(stderr, "stderr"); 658 | } 659 | 660 | pid_t 661 | fork_off_key_handler(void) 662 | { 663 | pid_t handler_pid = fork(); 664 | if (handler_pid == -1) { 665 | EPRINTF(0, "Can't fork(): %s.\n", strerror(errno)); 666 | exit(1); 667 | } 668 | 669 | if (handler_pid != 0) { 670 | return handler_pid; 671 | } 672 | 673 | // OK, we're the child agent process now... 674 | 675 | // Make this controllable by cmdline, do it only with -v 676 | redirect_stdall(); 677 | 678 | // Do the setsid thing 679 | if (setsid() == -1) { 680 | EPRINTF(0, "error from setsid(): %s.\n", strerror(errno)); 681 | exit(1); 682 | } 683 | 684 | chdir("/"); 685 | 686 | // TODO: Do the filehandle thing 687 | 688 | // TODO: Do setrlimit thing 689 | 690 | // TODO: Do the signal thing 691 | 692 | handle_key_requests_forever(); 693 | 694 | /* NOTREACHED */ 695 | exit(0); 696 | } 697 | 698 | void 699 | exec_subprocess(pid_t agent_pid) 700 | { 701 | close(listen_sock); 702 | 703 | if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) || 704 | setenv(SSH_AGENTPID_ENV_NAME, itoa_unsafe(agent_pid), 1)) { 705 | EPRINTF(0, "can't setenv - errno is %d.\n", errno); 706 | exit(1); 707 | } 708 | 709 | execvp(g_subprocess_argv[0], g_subprocess_argv); 710 | EPRINTF(0, "can't execvp(%s): %s.\n", 711 | g_subprocess_argv[0], strerror(errno)); 712 | exit(1); 713 | } 714 | 715 | int 716 | main(int argc, char **argv) 717 | { 718 | /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 719 | // TODO: sanitise_stdfd(); 720 | 721 | parse_cmdline(argc, argv); 722 | 723 | if (g_kill_flag) { 724 | kill_old_agent(); 725 | exit(0); 726 | } 727 | 728 | init_socket_list(); 729 | 730 | create_socket(); 731 | 732 | if (g_dontfork_flag) { 733 | // This is "debug" mode, but I prefer to call it dontfork mode... 734 | print_env_stuff(getpid()); 735 | handle_key_requests_forever(); 736 | /* NOTREACHED */ 737 | } else { 738 | pid_t agent_pid = fork_off_key_handler(); 739 | 740 | close(listen_sock); 741 | 742 | if (g_subprocess_argc) { 743 | exec_subprocess(agent_pid); 744 | /* NOTREACHED */ 745 | } else { 746 | print_env_stuff(agent_pid); 747 | remove_socket_at_exit = 0; 748 | exit(0); 749 | } 750 | /* NOTREACHED */ 751 | } 752 | /* NOTREACHED */ 753 | 754 | exit(0); 755 | } 756 | -------------------------------------------------------------------------------- /cmdline.c: -------------------------------------------------------------------------------- 1 | /* cmdline.c 2 | * 3 | * Parse command line arguments for charade. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "copyright.h" 12 | #include "cmdline.h" 13 | #include "eprintf.h" 14 | 15 | int g_csh_flag = 0; 16 | int g_sh_flag = 0; 17 | int g_kill_flag = 0; 18 | int g_dontfork_flag = 0; 19 | char *g_socket_name = 0; 20 | 21 | int g_subprocess_argc; 22 | char **g_subprocess_argv; 23 | 24 | static void 25 | usage(void) 26 | { 27 | EPRINTF(0, "usage: charade [options] [command [arg ...]]\n"); 28 | EPRINTF(0, "Options:\n"); 29 | EPRINTF(0, " -c Generate C-shell commands on stdout.\n"); 30 | EPRINTF(0, " -s Generate Bourne shell commands on stdout.\n"); 31 | EPRINTF(0, " -k Kill the current agent.\n"); 32 | EPRINTF(0, " -d Don't fork.\n"); 33 | EPRINTF(0, " -v [-v ...] More trace output.\n"); 34 | EPRINTF(0, " -a socket Bind agent socket to given name.\n"); 35 | EPRINTF(0, " --copyright Print out copyright message & licence.\n"); 36 | exit(1); 37 | } 38 | 39 | static struct option opts[] = { 40 | {"copyright", 0, NULL, 'C'}, 41 | {NULL, 0, NULL, 0} 42 | }; 43 | 44 | void 45 | parse_cmdline(int argc, char **argv) 46 | { 47 | int ch; 48 | 49 | /* I seem to get link errors when I declare these getopt variables. 50 | Things seem to work fine when I don't declare them, so I'm 51 | just commenting them out! 52 | extern int optind, opterr; 53 | extern char *optarg; 54 | */ 55 | 56 | while (-1 != (ch = getopt_long(argc, argv, "cskdva:", opts, NULL))) { 57 | switch (ch) { 58 | case 'c': ++g_csh_flag; 59 | break; 60 | case 's': ++g_sh_flag; 61 | break; 62 | case 'k': ++g_kill_flag; 63 | break; 64 | case 'd': g_dontfork_flag = 1; 65 | break; 66 | case 'v': louder(); 67 | break; 68 | case 'a': g_socket_name = optarg; 69 | break; 70 | case 'C': print_copyright(); 71 | break; 72 | default: usage(); 73 | } 74 | } 75 | argc -= optind; 76 | argv += optind; 77 | 78 | // If any flags given, there better not be process-to-run-args... 79 | if (argc > 0 && (g_csh_flag || g_sh_flag || g_kill_flag)) { 80 | EPRINTF(1, "Can't produce shell commands *and* run subprocess!\n"); 81 | usage(); 82 | } 83 | 84 | // If there's no shell and no process-to-run-args, assume C shell... 85 | if (argc == 0 && !g_csh_flag && !g_sh_flag) { 86 | char *shell = getenv("SHELL"); 87 | size_t len; 88 | if (shell != NULL && (len = strlen(shell)) > 2 89 | && strncmp(shell + len - 3, "csh", 3) == 0) 90 | g_csh_flag = 1; 91 | } 92 | 93 | if (argc > 0 && g_dontfork_flag) { 94 | EPRINTF(1, "Can't not fork *and* run a subprocess!\n"); 95 | usage(); 96 | } 97 | 98 | g_subprocess_argc = argc; 99 | g_subprocess_argv = argv; 100 | } 101 | -------------------------------------------------------------------------------- /cmdline.h: -------------------------------------------------------------------------------- 1 | /* cmdline.h 2 | * 3 | * Copyright (c) 2009, Wesley Darlington. All Rights Reserved. 4 | * 5 | */ 6 | 7 | #ifndef CMDLINE_H 8 | #define CMDLINE_H 9 | 10 | extern int g_csh_flag; 11 | extern int g_sh_flag; 12 | extern int g_kill_flag; 13 | extern int g_dontfork_flag; 14 | extern char *g_socket_name; 15 | 16 | extern int g_subprocess_argc; 17 | extern char **g_subprocess_argv; 18 | 19 | void parse_cmdline(int argc, char **argv); 20 | 21 | #endif // #ifndef CMDLINE_H 22 | -------------------------------------------------------------------------------- /contrib/charade-0.0.2-1.cygport: -------------------------------------------------------------------------------- 1 | NAME="charade" 2 | VERSION=0.0.2 3 | RELEASE=1 4 | CATEGORY="Net" 5 | SUMMARY="Ssh-agent clone for cygwin that proxies to pageant." 6 | DESCRIPTION="Charade just pretends to be ssh-agent on one side and putty on the other. It's a shim between openssh and pageant. 7 | 8 | It aspires to be a drop-in replacement for ssh-agent, and that's how I use it atm. It works for me. It probably won't work for you." 9 | 10 | HOMEPAGE="https://github.com/wesleyd/charade" 11 | 12 | GIT_URI="git://github.com/wesleyd/charade.git" 13 | #GIT_TAG="0.0.2" 14 | 15 | # Build dependencies only 16 | DEPEND="gcc-g++ make git" 17 | # runtime deps to go in setup.hint, and note the escaped newline 18 | REQUIRES="keychain openssh psmisc" 19 | 20 | 21 | inherit git 22 | 23 | # We use the standard src_compile and src_test. 24 | 25 | src_compile() { 26 | cd ${S} 27 | lndirs 28 | cd ${B} 29 | make 30 | } 31 | src_install() { 32 | cd ${B} 33 | dobin charade.exe 34 | insinto /etc/postinstall 35 | doins ${S}/charade_update_alternatives.sh 36 | } 37 | -------------------------------------------------------------------------------- /contrib/charade-0.0.2-1.src.patch: -------------------------------------------------------------------------------- 1 | diff --git a/charade_update_alternatives.sh b/charade_update_alternatives.sh 2 | new file mode 100644 3 | index 0000000..73369cb 4 | --- /dev/null 5 | +++ charade_update_alternatives.sh 6 | @@ -0,0 +1,12 @@ 7 | +#!/bin/bash 8 | + 9 | +set -e 10 | + 11 | +if [ -f /usr/bin/ssh-agent.exe ]; then 12 | + echo "Moving old ssh-agent.exe" 13 | + mv -v /usr/bin/ssh-agent.exe /usr/bin/openssh-agent.exe 14 | + /usr/sbin/alternatives.exe --install /usr/bin/ssh-agent ssh-agent /usr/bin/openssh-agent.exe 100 15 | +fi; 16 | + 17 | +/usr/sbin/alternatives.exe --install /usr/bin/ssh-agent ssh-agent /usr/bin/charade.exe 80 18 | +/usr/sbin/alternatives.exe --set ssh-agent /usr/bin/charade.exe 19 | -------------------------------------------------------------------------------- /copyright.c: -------------------------------------------------------------------------------- 1 | 2 | /* AUTO GENERATED FILE - DO NOT MODIFY - SEE ./generate-copyright.pl FOR DETAILS */ 3 | 4 | #include 5 | 6 | #include "copyright.h" 7 | #include "eprintf.h" 8 | 9 | void 10 | print_copyright(void) 11 | { 12 | EPRINTF_RAW(0, "\n"); 13 | EPRINTF_RAW(0, "Charade is Copyright (c) 2009 Wesley Darlington.\n"); 14 | EPRINTF_RAW(0, "\n"); 15 | EPRINTF_RAW(0, "Portions are:\n"); 16 | EPRINTF_RAW(0, " Copyright 1997-2007 Simon Tatham.\n"); 17 | EPRINTF_RAW(0, " Copyright (c) 1995 Tatu Ylonen , Espoo, Finland. All Rights Reserved.\n"); 18 | EPRINTF_RAW(0, " Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.\n"); 19 | EPRINTF_RAW(0, "\n"); 20 | EPRINTF_RAW(0, "Redistribution and use in source and binary forms, with or without\n"); 21 | EPRINTF_RAW(0, "modification, are permitted provided that the following conditions\n"); 22 | EPRINTF_RAW(0, "are met:\n"); 23 | EPRINTF_RAW(0, "1. Redistributions of source code must retain the above copyright\n"); 24 | EPRINTF_RAW(0, " notice, this list of conditions and the following disclaimer.\n"); 25 | EPRINTF_RAW(0, "2. Redistributions in binary form must reproduce the above copyright\n"); 26 | EPRINTF_RAW(0, " notice, this list of conditions and the following disclaimer in the\n"); 27 | EPRINTF_RAW(0, " documentation and/or other materials provided with the distribution.\n"); 28 | EPRINTF_RAW(0, "\n"); 29 | EPRINTF_RAW(0, "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"); 30 | EPRINTF_RAW(0, "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"); 31 | EPRINTF_RAW(0, "OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"); 32 | EPRINTF_RAW(0, "IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"); 33 | EPRINTF_RAW(0, "INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n"); 34 | EPRINTF_RAW(0, "NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"); 35 | EPRINTF_RAW(0, "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"); 36 | EPRINTF_RAW(0, "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"); 37 | EPRINTF_RAW(0, "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n"); 38 | EPRINTF_RAW(0, "THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"); 39 | 40 | EPRINTF_RAW(0, "\n"); 41 | 42 | exit(0); 43 | } 44 | -------------------------------------------------------------------------------- /copyright.h: -------------------------------------------------------------------------------- 1 | /* copyright.h 2 | * 3 | * Header file for automatically generated (from file "LICENCE") 4 | * copyright notice file. 5 | * 6 | * Copyright (c) 2009, Wesley Darlington. All Rights Reserved. 7 | */ 8 | 9 | #ifndef COPYRIGHT_C 10 | #define COPYRIGHT_C 11 | 12 | void print_copyright(void); 13 | 14 | #endif // #ifndef COPYRIGHT_C 15 | -------------------------------------------------------------------------------- /depend.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DIR="$1" 4 | shift 1 5 | case "$DIR" in 6 | "" | "." ) 7 | $CC -MM -MG "$@" | sed -e 's@^\(.*\)\.o:@\1.d \1.o:@' 8 | ;; 9 | *) 10 | $CC -MM -MG "$@" | sed -e 's@^\(.*\)\.o:@$DIR/\1.d $DIR/\1.o:@' 11 | ;; 12 | esac 13 | -------------------------------------------------------------------------------- /eprintf.c: -------------------------------------------------------------------------------- 1 | /* eprintf.c 2 | * 3 | * Definitions of various clean output functions 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | #include "eprintf.h" 10 | 11 | static int g_volume = 0; 12 | 13 | void 14 | louder(void) 15 | { 16 | ++g_volume; 17 | } 18 | 19 | int 20 | get_loudness(void) 21 | { 22 | return g_volume; 23 | } 24 | 25 | 26 | int 27 | eprintf(int level, const char *fmt, ...) 28 | { 29 | if (level > g_volume) 30 | return 0; 31 | 32 | va_list ap; 33 | va_start(ap, fmt); 34 | 35 | int rv = vfprintf(stderr, fmt, ap); 36 | // fflush(stderr); 37 | 38 | va_end(ap); 39 | 40 | return rv; 41 | } 42 | -------------------------------------------------------------------------------- /eprintf.h: -------------------------------------------------------------------------------- 1 | #ifndef EPRINTF_H 2 | #define EPRINTF_H 3 | 4 | /* eprintf.h 5 | * 6 | */ 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif // #ifdef __cplusplus 11 | 12 | #define EPRINTF(Level, Format, Args...) \ 13 | eprintf(Level, "%s: " Format, __func__, ##Args) 14 | 15 | #define EPRINTF_RAW(Level, Format, Args...) \ 16 | eprintf(Level, Format, ##Args) 17 | 18 | int eprintf(int level, const char *fmt, ...) 19 | __attribute__ ((format (printf, 2, 3))); 20 | 21 | void louder(void); 22 | int get_loudness(void); 23 | 24 | #ifdef __cplusplus 25 | } 26 | #endif // #ifdef __cplusplus 27 | 28 | #endif // #define EPRINTF_H 29 | -------------------------------------------------------------------------------- /generate-copyright.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | # 3 | # Generate a .c file with a print_copyright() function in it to print 4 | # out the contents of all the files on the command line... 5 | # 6 | # Copyright (c) 2009, Wesley Darlington. All Rights Reserved. 7 | 8 | use warnings; 9 | use strict; 10 | 11 | die "usage: $0 LICENCE ...\n" unless @ARGV; 12 | 13 | sub c_stringify { 14 | my $line = shift; 15 | chomp($line); 16 | 17 | $line =~ s/\\/\\\\/; 18 | $line =~ s/"/\\"/; 19 | $line =~ s/\n/"\n"/g; 20 | 21 | return $line; 22 | } 23 | 24 | print qq{ 25 | /* AUTO GENERATED FILE - DO NOT MODIFY - SEE $0 FOR DETAILS */ 26 | 27 | #include 28 | 29 | #include "copyright.h" 30 | #include "eprintf.h" 31 | 32 | void 33 | print_copyright(void) 34 | \{ 35 | EPRINTF_RAW(0, "\\n"); 36 | }; 37 | 38 | while (<>) { 39 | my $safe = c_stringify($_); 40 | print " EPRINTF_RAW(0, \"" . c_stringify($safe) . "\\n\");\n"; 41 | } 42 | 43 | print qq{ 44 | EPRINTF_RAW(0, "\\n"); 45 | 46 | exit(0); 47 | \} 48 | }; 49 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | echo -n "Installing charade into /usr/bin ... " 6 | install charade.exe /usr/bin 7 | echo done 8 | 9 | echo "Backing up old /usr/bin/ssh-agent.exe ..." 10 | if [ -e /usr/bin/ssh-agent.exe ]; then 11 | if [ -f /usr/bin/ssh-agent-orig.exe ]; then 12 | echo -n " Backup already exists: just removing old ssh-agent.exe ..." 13 | rm /usr/bin/ssh-agent.exe 14 | echo done 15 | else 16 | echo -n " Moving ssh-agent.exe to ssh-agent-orig.exe ... " 17 | mv /usr/bin/ssh-agent.exe /usr/bin/ssh-agent-orig.exe 18 | echo done 19 | fi 20 | else 21 | echo "No need to backup old ssh-agent - it doesn't exist. Hmmm." 22 | fi 23 | 24 | echo -n "Making ssh-agent point symlinkishly to charade ... " 25 | ln -s /usr/bin/charade.exe /usr/bin/ssh-agent.exe 26 | echo done 27 | -------------------------------------------------------------------------------- /pageant.c: -------------------------------------------------------------------------------- 1 | /* The code in here is mostly lifted from Simon Tatham's putty code, in 2 | * particular windows/winpgntc.c. 3 | * 4 | * Copyright (c) 2009, Wesley Darlington. All Rights Reserved. 5 | * 6 | * Large swathes of the pageant interface taken from the putty source 7 | * code... 8 | * 9 | * Copyright 1997-2007 Simon Tatham. 10 | * 11 | * Portions copyright Robert de Bath, Joris van Rantwijk, Delian 12 | * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, 13 | * Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus 14 | * Kuhn, and CORE SDI S.A. 15 | * 16 | * Permission is hereby granted, free of charge, to any person 17 | * obtaining a copy of this software and associated documentation files 18 | * (the "Software"), to deal in the Software without restriction, 19 | * including without limitation the rights to use, copy, modify, merge, 20 | * publish, distribute, sublicense, and/or sell copies of the Software, 21 | * and to permit persons to whom the Software is furnished to do so, 22 | * subject to the following conditions: 23 | * 24 | * The above copyright notice and this permission notice shall be 25 | * included in all copies or substantial portions of the Software. 26 | * 27 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 28 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 29 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 30 | * NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE 31 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 32 | * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 33 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 | */ 35 | 36 | #include 37 | #include 38 | 39 | #include "eprintf.h" 40 | #include "pageant.h" 41 | 42 | #ifndef NO_SECURITY 43 | #include 44 | #endif 45 | 46 | // TODO: is there actually something magical about this number? 47 | #define AGENT_COPYDATA_ID 0x804e50ba 48 | 49 | #define AGENT_MAX_MSGLEN 8192 50 | 51 | /* 52 | * Dynamically load advapi32.dll for SID manipulation. In its absence, 53 | * we degrade gracefully. 54 | */ 55 | #ifndef NO_SECURITY 56 | int advapi_initialised = FALSE; 57 | static HMODULE advapi; 58 | DECL_WINDOWS_FUNCTION(static, BOOL, OpenProcessToken, 59 | (HANDLE, DWORD, PHANDLE)); 60 | DECL_WINDOWS_FUNCTION(static, BOOL, GetTokenInformation, 61 | (HANDLE, TOKEN_INFORMATION_CLASS, 62 | LPVOID, DWORD, PDWORD)); 63 | DECL_WINDOWS_FUNCTION(static, BOOL, InitializeSecurityDescriptor, 64 | (PSECURITY_DESCRIPTOR, DWORD)); 65 | DECL_WINDOWS_FUNCTION(static, BOOL, SetSecurityDescriptorOwner, 66 | (PSECURITY_DESCRIPTOR, PSID, BOOL)); 67 | static int init_advapi(void) 68 | { 69 | advapi = load_system32_dll("advapi32.dll"); 70 | return advapi && 71 | GET_WINDOWS_FUNCTION(advapi, OpenProcessToken) && 72 | GET_WINDOWS_FUNCTION(advapi, GetTokenInformation) && 73 | GET_WINDOWS_FUNCTION(advapi, InitializeSecurityDescriptor) && 74 | GET_WINDOWS_FUNCTION(advapi, SetSecurityDescriptorOwner); 75 | } 76 | #endif 77 | 78 | HMODULE load_system32_dll(const char *libname) 79 | { 80 | /* 81 | * Wrapper function to load a DLL out of c:\windows\system32 82 | * without going through the full DLL search path. (Hence no 83 | * attack is possible by placing a substitute DLL earlier on that 84 | * path.) 85 | */ 86 | static char path[MAX_PATH * 2] = {0}; 87 | unsigned int len = GetSystemDirectory(path, MAX_PATH); 88 | if (len == 0 || len > MAX_PATH) return NULL; 89 | 90 | strcat(path, "\\"); 91 | strcat(path, libname); 92 | HMODULE ret = LoadLibrary(path); 93 | return ret; 94 | } 95 | 96 | void 97 | print_buf(int level, byte *buf, int numbytes) 98 | { 99 | int i; 100 | for (i = 0; i < numbytes;) { 101 | EPRINTF_RAW(level, "%02x ", buf[i]); 102 | ++i; 103 | if (!(i%8)) EPRINTF_RAW(level, " "); 104 | if (!(i%16) || i == numbytes) EPRINTF_RAW(level, "\n"); 105 | } 106 | } 107 | 108 | BOOL CALLBACK 109 | wnd_enum_proc(HWND hwnd, LPARAM lparam) 110 | { 111 | char window_name[512] = "\0"; 112 | GetWindowText(hwnd, window_name, 512); 113 | EPRINTF(0, "Window %p: title '%s'.\n", hwnd, window_name); 114 | return TRUE; 115 | } 116 | 117 | void 118 | enum_windows(void) 119 | { 120 | BOOL retval = EnumWindows(wnd_enum_proc, (LPARAM) 0); 121 | 122 | if (!retval) { 123 | EPRINTF(0, "EnumWindows failed; GetLastError() => %d.\n", 124 | (int) GetLastError()); 125 | } 126 | } 127 | 128 | // "in" means "to pageant", "out" means "from pageant". Sorry. 129 | int 130 | send_request_to_pageant(byte *inbuf, int inbytes, byte *outbuf, int outbuflen) 131 | { 132 | EPRINTF(3, "Sending %d bytes to pageant.\n", inbytes); 133 | 134 | if (inbytes < 4) { 135 | EPRINTF(0, "Pageant-bound message too short (%d bytes).\n", inbytes); 136 | return 0; 137 | } 138 | int claimed_inbytes = GET_32BIT(inbuf); 139 | if (inbytes != claimed_inbytes + 4) { 140 | EPRINTF(0, "Pageant-bound message is %d bytes long, but it " 141 | "*says* it has %d=%d+4 bytes in it.\n", 142 | inbytes, claimed_inbytes+4, claimed_inbytes); 143 | return 0; 144 | } 145 | 146 | EPRINTF(5, "Message to pageant (%d bytes):\n", inbytes); 147 | print_buf(5, inbuf, inbytes); 148 | 149 | HWND hwnd; 150 | hwnd = FindWindow("Pageant", "Pageant"); 151 | if (!hwnd) { 152 | EPRINTF(0, "Can't FindWindow(\"Pageant\"...) - " 153 | "is pageant running?. (GetLastError is %x)\n", 154 | (unsigned) GetLastError()); 155 | return 0; 156 | } 157 | 158 | char mapname[512]; 159 | sprintf(mapname, "PageantRequest%08x", (unsigned)GetCurrentThreadId()); 160 | 161 | PSECURITY_ATTRIBUTES psa = NULL; 162 | #ifndef NO_SECURITY 163 | SECURITY_ATTRIBUTES sa = {0}; 164 | if (advapi_initialised || init_advapi()) { 165 | /* 166 | * Set the security info for the file mapping to the same as Pageant's 167 | * process, to make sure Pageant's SID check will pass. 168 | */ 169 | 170 | DWORD dwProcId = 0; 171 | GetWindowThreadProcessId(hwnd, &dwProcId); 172 | HANDLE proc = OpenProcess(MAXIMUM_ALLOWED, FALSE, dwProcId); 173 | if (proc != NULL) { 174 | sa.nLength = sizeof(sa); 175 | sa.bInheritHandle = TRUE; 176 | sa.lpSecurityDescriptor = NULL; 177 | GetSecurityInfo(proc, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION, 178 | NULL, NULL, NULL, NULL, (PSECURITY_DESCRIPTOR*)&sa.lpSecurityDescriptor); 179 | if (sa.lpSecurityDescriptor) { 180 | psa = &sa; 181 | } 182 | } 183 | CloseHandle(proc); 184 | } else { 185 | EPRINTF(0, "Couldn't initialize advapi.\n"); 186 | } 187 | #endif /* NO_SECURITY */ 188 | 189 | HANDLE filemap = CreateFileMapping(INVALID_HANDLE_VALUE, psa, 190 | PAGE_READWRITE, 0, 191 | AGENT_MAX_MSGLEN, mapname); 192 | if (filemap == NULL || filemap == INVALID_HANDLE_VALUE) { 193 | EPRINTF(0, "Can't CreateFileMapping.\n"); 194 | return 0; 195 | } 196 | 197 | byte *shmem = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0); 198 | memcpy(shmem, inbuf, inbytes); 199 | COPYDATASTRUCT cds; 200 | cds.dwData = AGENT_COPYDATA_ID; 201 | cds.cbData = 1 + strlen(mapname); 202 | cds.lpData = mapname; 203 | 204 | int id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds); 205 | int retlen = 0; 206 | if (id > 0) { 207 | retlen = 4 + GET_32BIT(shmem); 208 | if (retlen > outbuflen) { 209 | EPRINTF(0, "Buffer too small to contain reply from pageant.\n"); 210 | return 0; 211 | } 212 | 213 | memcpy(outbuf, shmem, retlen); 214 | 215 | EPRINTF(5, "Reply from pageant (%d bytes):\n", retlen); 216 | print_buf(5, outbuf, retlen); 217 | } else { 218 | EPRINTF(0, "Couldn't SendMessage().\n"); 219 | return 0; 220 | } 221 | 222 | // enum_windows(); 223 | 224 | 225 | UnmapViewOfFile(shmem); 226 | CloseHandle(filemap); 227 | #ifndef NO_SECURITY 228 | if (sa.lpSecurityDescriptor) 229 | LocalFree(sa.lpSecurityDescriptor); 230 | #endif 231 | 232 | EPRINTF(3, "Got %d bytes back from pageant.\n", retlen); 233 | 234 | return retlen; 235 | } 236 | -------------------------------------------------------------------------------- /pageant.h: -------------------------------------------------------------------------------- 1 | #ifndef PAGEANT_H 2 | #define PAGEANT_H 3 | 4 | /* The code in here is mostly lifted from Simon Tatham's putty code, in 5 | * particular windows/winpgntc.c. 6 | * 7 | * Copyright (c) 2009, Wesley Darlington. All Rights Reserved. 8 | * 9 | * Large swathes of the pageant interface taken from the putty source 10 | * code... 11 | * 12 | * Copyright 1997-2007 Simon Tatham. 13 | * 14 | * Portions copyright Robert de Bath, Joris van Rantwijk, Delian 15 | * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, 16 | * Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus 17 | * Kuhn, and CORE SDI S.A. 18 | * 19 | * Permission is hereby granted, free of charge, to any person 20 | * obtaining a copy of this software and associated documentation files 21 | * (the "Software"), to deal in the Software without restriction, 22 | * including without limitation the rights to use, copy, modify, merge, 23 | * publish, distribute, sublicense, and/or sell copies of the Software, 24 | * and to permit persons to whom the Software is furnished to do so, 25 | * subject to the following conditions: 26 | * 27 | * The above copyright notice and this permission notice shall be 28 | * included in all copies or substantial portions of the Software. 29 | * 30 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 31 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 32 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 33 | * NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE 34 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 35 | * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 36 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 37 | */ 38 | 39 | #define GET_32BIT_MSB_FIRST(cp) \ 40 | (((unsigned long)(unsigned char)(cp)[0] << 24) | \ 41 | ((unsigned long)(unsigned char)(cp)[1] << 16) | \ 42 | ((unsigned long)(unsigned char)(cp)[2] << 8) | \ 43 | ((unsigned long)(unsigned char)(cp)[3] )) 44 | 45 | #define GET_32BIT(cp) GET_32BIT_MSB_FIRST(cp) 46 | 47 | /* 48 | * Dynamically linked functions. These come in two flavours: 49 | * 50 | * - GET_WINDOWS_FUNCTION does not expose "name" to the preprocessor, 51 | * so will always dynamically link against exactly what is specified 52 | * in "name". If you're not sure, use this one. 53 | * 54 | * - GET_WINDOWS_FUNCTION_PP allows "name" to be redirected via 55 | * preprocessor definitions like "#define foo bar"; this is principally 56 | * intended for the ANSI/Unicode DoSomething/DoSomethingA/DoSomethingW. 57 | * If your function has an argument of type "LPTSTR" or similar, this 58 | * is the variant to use. 59 | * (However, it can't always be used, as it trips over more complicated 60 | * macro trickery such as the WspiapiGetAddrInfo wrapper for getaddrinfo.) 61 | * 62 | * (DECL_WINDOWS_FUNCTION works with both these variants.) 63 | */ 64 | #define DECL_WINDOWS_FUNCTION(linkage, rettype, name, params) \ 65 | typedef rettype (WINAPI *t_##name) params; \ 66 | linkage t_##name p_##name 67 | #define STR1(x) #x 68 | #define STR(x) STR1(x) 69 | #define GET_WINDOWS_FUNCTION_PP(module, name) \ 70 | (p_##name = module ? (t_##name) GetProcAddress(module, STR(name)) : NULL) 71 | #define GET_WINDOWS_FUNCTION(module, name) \ 72 | (p_##name = module ? (t_##name) GetProcAddress(module, #name) : NULL) 73 | 74 | HMODULE load_system32_dll(const char *libname); 75 | 76 | // Send a numbytes-sized message to pageant, synchronously. 77 | // Up to outbuflen bytes of response will be written into outbuf. 78 | // If the returned message would overflow buf, or if any other 79 | // error occurs, 0 will be returned. 80 | int send_request_to_pageant(byte *inbuf, int inbytes, 81 | byte *outbuf, int outbuflen); 82 | 83 | #endif // #ifndef PAGEANT_H 84 | -------------------------------------------------------------------------------- /todo.h: -------------------------------------------------------------------------------- 1 | #ifndef TODO_H 2 | #define TODO_H 3 | 4 | #include "eprintf.h" 5 | 6 | #define TODO() do { EPRINTF(0, "TODO.\n"); exit(1); } while (1) 7 | 8 | #endif // #ifndef TODO_H 9 | --------------------------------------------------------------------------------