1 /*
2 * prompt.c -- ask the user for authentication information.
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24 /* ==================================================================== */
25
26
27
28 /*** Includes. ***/
29
30 #include <apr_lib.h>
31 #include <apr_poll.h>
32 #include <apr_portable.h>
33
34 #include "svn_cmdline.h"
35 #include "svn_ctype.h"
36 #include "svn_string.h"
37 #include "svn_auth.h"
38 #include "svn_error.h"
39 #include "svn_path.h"
40
41 #include "private/svn_cmdline_private.h"
42 #include "svn_private_config.h"
43
44 #ifdef WIN32
45 #include <conio.h>
46 #elif defined(HAVE_TERMIOS_H)
47 #include <signal.h>
48 #include <termios.h>
49 #endif
50
51
52
53 /* Descriptor of an open terminal */
54 typedef struct terminal_handle_t terminal_handle_t;
55 struct terminal_handle_t
56 {
57 apr_file_t *infd; /* input file handle */
58 apr_file_t *outfd; /* output file handle */
59 svn_boolean_t noecho; /* terminal echo was turned off */
60 svn_boolean_t close_handles; /* close handles when closing the terminal */
61 apr_pool_t *pool; /* pool associated with the file handles */
62
63 #ifdef HAVE_TERMIOS_H
64 svn_boolean_t restore_state; /* terminal state was changed */
65 apr_os_file_t osinfd; /* OS-specific handle for infd */
66 struct termios attr; /* saved terminal attributes */
67 #endif
68 };
69
70 /* Initialize safe state of terminal_handle_t. */
71 static void
terminal_handle_init(terminal_handle_t * terminal,apr_file_t * infd,apr_file_t * outfd,svn_boolean_t noecho,svn_boolean_t close_handles,apr_pool_t * pool)72 terminal_handle_init(terminal_handle_t *terminal,
73 apr_file_t *infd, apr_file_t *outfd,
74 svn_boolean_t noecho, svn_boolean_t close_handles,
75 apr_pool_t *pool)
76 {
77 memset(terminal, 0, sizeof(*terminal));
78 terminal->infd = infd;
79 terminal->outfd = outfd;
80 terminal->noecho = noecho;
81 terminal->close_handles = close_handles;
82 terminal->pool = pool;
83 }
84
85 /*
86 * Common pool cleanup handler for terminal_handle_t. Closes TERMINAL.
87 * If CLOSE_HANDLES is TRUE, close the terminal file handles.
88 * If RESTORE_STATE is TRUE, restores the TERMIOS flags of the terminal.
89 */
90 static apr_status_t
terminal_cleanup_handler(terminal_handle_t * terminal,svn_boolean_t close_handles,svn_boolean_t restore_state)91 terminal_cleanup_handler(terminal_handle_t *terminal,
92 svn_boolean_t close_handles,
93 svn_boolean_t restore_state)
94 {
95 apr_status_t status = APR_SUCCESS;
96
97 #ifdef HAVE_TERMIOS_H
98 /* Restore terminal state flags. */
99 if (restore_state && terminal->restore_state)
100 tcsetattr(terminal->osinfd, TCSANOW, &terminal->attr);
101 #endif
102
103 /* Close terminal handles. */
104 if (close_handles && terminal->close_handles)
105 {
106 apr_file_t *const infd = terminal->infd;
107 apr_file_t *const outfd = terminal->outfd;
108
109 if (infd)
110 {
111 terminal->infd = NULL;
112 status = apr_file_close(infd);
113 }
114
115 if (!status && outfd && outfd != infd)
116 {
117 terminal->outfd = NULL;
118 status = apr_file_close(terminal->outfd);
119 }
120 }
121 return status;
122 }
123
124 /* Normal pool cleanup for a terminal. */
terminal_plain_cleanup(void * baton)125 static apr_status_t terminal_plain_cleanup(void *baton)
126 {
127 return terminal_cleanup_handler(baton, FALSE, TRUE);
128 }
129
130 /* Child pool cleanup for a terminal -- does not restore echo state. */
terminal_child_cleanup(void * baton)131 static apr_status_t terminal_child_cleanup(void *baton)
132 {
133 return terminal_cleanup_handler(baton, FALSE, FALSE);
134 }
135
136 /* Explicitly close the terminal, removing its cleanup handlers. */
137 static svn_error_t *
terminal_close(terminal_handle_t * terminal)138 terminal_close(terminal_handle_t *terminal)
139 {
140 apr_status_t status;
141
142 /* apr_pool_cleanup_kill() removes both normal and child cleanup */
143 apr_pool_cleanup_kill(terminal->pool, terminal, terminal_plain_cleanup);
144
145 status = terminal_cleanup_handler(terminal, TRUE, TRUE);
146 if (status)
147 return svn_error_create(status, NULL, _("Can't close terminal"));
148 return SVN_NO_ERROR;
149 }
150
151 /* Allocate and open *TERMINAL. If NOECHO is TRUE, try to turn off
152 terminal echo. Use POOL for all allocations.*/
153 static svn_error_t *
terminal_open(terminal_handle_t ** terminal,svn_boolean_t noecho,apr_pool_t * pool)154 terminal_open(terminal_handle_t **terminal, svn_boolean_t noecho,
155 apr_pool_t *pool)
156 {
157 apr_status_t status;
158
159 #ifdef WIN32
160 /* On Windows, we'll use the console API directly if the process has
161 a console attached; otherwise we'll just use stdin and stderr. */
162 const HANDLE conin = CreateFileW(L"CONIN$", GENERIC_READ,
163 FILE_SHARE_READ | FILE_SHARE_WRITE,
164 NULL, OPEN_EXISTING,
165 FILE_ATTRIBUTE_NORMAL, NULL);
166 *terminal = apr_palloc(pool, sizeof(terminal_handle_t));
167 if (conin != INVALID_HANDLE_VALUE)
168 {
169 /* The process has a console. */
170 CloseHandle(conin);
171 terminal_handle_init(*terminal, NULL, NULL, noecho, FALSE, NULL);
172 return SVN_NO_ERROR;
173 }
174 #else /* !WIN32 */
175 /* Without evidence to the contrary, we'll assume this is *nix and
176 try to open /dev/tty. If that fails, we'll use stdin for input
177 and stderr for prompting. */
178 apr_file_t *tmpfd;
179 status = apr_file_open(&tmpfd, "/dev/tty",
180 APR_FOPEN_READ | APR_FOPEN_WRITE,
181 APR_OS_DEFAULT, pool);
182 *terminal = apr_palloc(pool, sizeof(terminal_handle_t));
183 if (!status)
184 {
185 /* We have a terminal handle that we can use for input and output. */
186 terminal_handle_init(*terminal, tmpfd, tmpfd, FALSE, TRUE, pool);
187 }
188 #endif /* !WIN32 */
189 else
190 {
191 /* There is no terminal. Sigh. */
192 apr_file_t *infd;
193 apr_file_t *outfd;
194
195 status = apr_file_open_stdin(&infd, pool);
196 if (status)
197 return svn_error_wrap_apr(status, _("Can't open stdin"));
198 status = apr_file_open_stderr(&outfd, pool);
199 if (status)
200 return svn_error_wrap_apr(status, _("Can't open stderr"));
201 terminal_handle_init(*terminal, infd, outfd, FALSE, FALSE, pool);
202 }
203
204 #ifdef HAVE_TERMIOS_H
205 /* Set terminal state */
206 if (0 == apr_os_file_get(&(*terminal)->osinfd, (*terminal)->infd))
207 {
208 if (0 == tcgetattr((*terminal)->osinfd, &(*terminal)->attr))
209 {
210 struct termios attr = (*terminal)->attr;
211 /* Turn off signal handling and canonical input mode */
212 attr.c_lflag &= ~(ISIG | ICANON);
213 attr.c_cc[VMIN] = 1; /* Read one byte at a time */
214 attr.c_cc[VTIME] = 0; /* No timeout, wait indefinitely */
215 attr.c_lflag &= ~(ECHO); /* Turn off echo */
216 if (0 == tcsetattr((*terminal)->osinfd, TCSAFLUSH, &attr))
217 {
218 (*terminal)->noecho = noecho;
219 (*terminal)->restore_state = TRUE;
220 }
221 }
222 }
223 #endif /* HAVE_TERMIOS_H */
224
225 /* Register pool cleanup to close handles and restore echo state. */
226 apr_pool_cleanup_register((*terminal)->pool, *terminal,
227 terminal_plain_cleanup,
228 terminal_child_cleanup);
229 return SVN_NO_ERROR;
230 }
231
232 /* Write a null-terminated STRING to TERMINAL.
233 Use POOL for allocations related to converting STRING from UTF-8. */
234 static svn_error_t *
terminal_puts(const char * string,terminal_handle_t * terminal,apr_pool_t * pool)235 terminal_puts(const char *string, terminal_handle_t *terminal,
236 apr_pool_t *pool)
237 {
238 svn_error_t *err;
239 const char *converted;
240
241 err = svn_cmdline_cstring_from_utf8(&converted, string, pool);
242 if (err)
243 {
244 svn_error_clear(err);
245 converted = svn_cmdline_cstring_from_utf8_fuzzy(string, pool);
246 }
247
248 #ifdef WIN32
249 if (!terminal->outfd)
250 {
251 /* See terminal_open; we're using Console I/O. */
252 _cputs(converted);
253 return SVN_NO_ERROR;
254 }
255 #endif
256
257 SVN_ERR(svn_io_file_write_full(terminal->outfd, converted,
258 strlen(converted), NULL, pool));
259
260 return svn_error_trace(svn_io_file_flush(terminal->outfd, pool));
261 }
262
263 /* These codes can be returned from terminal_getc instead of a character. */
264 #define TERMINAL_NONE 0x80000 /* no character read, retry */
265 #define TERMINAL_DEL (TERMINAL_NONE + 1) /* the input was a deleteion */
266 #define TERMINAL_EOL (TERMINAL_NONE + 2) /* end of input/end of line */
267 #define TERMINAL_EOF (TERMINAL_NONE + 3) /* end of file during input */
268
269 /* Helper for terminal_getc: writes CH to OUTFD as a control char. */
270 #ifndef WIN32
271 static void
echo_control_char(char ch,apr_file_t * outfd)272 echo_control_char(char ch, apr_file_t *outfd)
273 {
274 if (svn_ctype_iscntrl(ch))
275 {
276 const char substitute = (ch < 32? '@' + ch : '?');
277 apr_file_putc('^', outfd);
278 apr_file_putc(substitute, outfd);
279 }
280 else if (svn_ctype_isprint(ch))
281 {
282 /* Pass printable characters unchanged. */
283 apr_file_putc(ch, outfd);
284 }
285 else
286 {
287 /* Everything else is strange. */
288 apr_file_putc('^', outfd);
289 apr_file_putc('!', outfd);
290 }
291 }
292 #endif /* WIN32 */
293
294 /* Read one character or control code from TERMINAL, returning it in CODE.
295 if CAN_ERASE and the input was a deletion, emit codes to erase the
296 last character displayed on the terminal.
297 Use POOL for all allocations. */
298 static svn_error_t *
terminal_getc(int * code,terminal_handle_t * terminal,svn_boolean_t can_erase,apr_pool_t * pool)299 terminal_getc(int *code, terminal_handle_t *terminal,
300 svn_boolean_t can_erase, apr_pool_t *pool)
301 {
302 const svn_boolean_t echo = !terminal->noecho;
303 apr_status_t status = APR_SUCCESS;
304 char ch;
305
306 #ifdef WIN32
307 if (!terminal->infd)
308 {
309 /* See terminal_open; we're using Console I/O. */
310
311 /* The following was hoisted from APR's getpass for Windows. */
312 int concode = _getch();
313 switch (concode)
314 {
315 case '\r': /* end-of-line */
316 *code = TERMINAL_EOL;
317 if (echo)
318 _cputs("\r\n");
319 break;
320
321 case EOF: /* end-of-file */
322 case 26: /* Ctrl+Z */
323 *code = TERMINAL_EOF;
324 if (echo)
325 _cputs((concode == EOF ? "[EOF]\r\n" : "^Z\r\n"));
326 break;
327
328 case 3: /* Ctrl+C, Ctrl+Break */
329 /* _getch() bypasses Ctrl+C but not Ctrl+Break detection! */
330 if (echo)
331 _cputs("^C\r\n");
332 return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL);
333
334 case 0: /* Function code prefix */
335 case 0xE0:
336 concode = (concode << 4) | _getch();
337 /* Catch {DELETE}, {<--}, Num{DEL} and Num{<--} */
338 if (concode == 0xE53 || concode == 0xE4B
339 || concode == 0x053 || concode == 0x04B)
340 {
341 *code = TERMINAL_DEL;
342 if (can_erase)
343 _cputs("\b \b");
344 }
345 else
346 {
347 *code = TERMINAL_NONE;
348 _putch('\a');
349 }
350 break;
351
352 case '\b': /* BS */
353 case 127: /* DEL */
354 *code = TERMINAL_DEL;
355 if (can_erase)
356 _cputs("\b \b");
357 break;
358
359 default:
360 if (!apr_iscntrl(concode))
361 {
362 *code = (int)(unsigned char)concode;
363 _putch(echo ? concode : '*');
364 }
365 else
366 {
367 *code = TERMINAL_NONE;
368 _putch('\a');
369 }
370 }
371 return SVN_NO_ERROR;
372 }
373 #elif defined(HAVE_TERMIOS_H)
374 if (terminal->restore_state)
375 {
376 /* We're using a bytewise-immediate termios input */
377 const struct termios *const attr = &terminal->attr;
378
379 status = apr_file_getc(&ch, terminal->infd);
380 if (status)
381 return svn_error_wrap_apr(status, _("Can't read from terminal"));
382
383 if (ch == attr->c_cc[VINTR] || ch == attr->c_cc[VQUIT])
384 {
385 /* Break */
386 echo_control_char(ch, terminal->outfd);
387 return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL);
388 }
389 else if (ch == '\r' || ch == '\n' || ch == attr->c_cc[VEOL])
390 {
391 /* Newline */
392 *code = TERMINAL_EOL;
393 apr_file_putc('\n', terminal->outfd);
394 }
395 else if (ch == '\b' || ch == attr->c_cc[VERASE])
396 {
397 /* Delete */
398 *code = TERMINAL_DEL;
399 if (can_erase)
400 {
401 apr_file_putc('\b', terminal->outfd);
402 apr_file_putc(' ', terminal->outfd);
403 apr_file_putc('\b', terminal->outfd);
404 }
405 }
406 else if (ch == attr->c_cc[VEOF])
407 {
408 /* End of input */
409 *code = TERMINAL_EOF;
410 echo_control_char(ch, terminal->outfd);
411 }
412 else if (ch == attr->c_cc[VSUSP])
413 {
414 /* Suspend */
415 *code = TERMINAL_NONE;
416 kill(0, SIGTSTP);
417 }
418 else if (!apr_iscntrl(ch))
419 {
420 /* Normal character */
421 *code = (int)(unsigned char)ch;
422 apr_file_putc((echo ? ch : '*'), terminal->outfd);
423 }
424 else
425 {
426 /* Ignored character */
427 *code = TERMINAL_NONE;
428 apr_file_putc('\a', terminal->outfd);
429 }
430 return SVN_NO_ERROR;
431 }
432 #endif /* HAVE_TERMIOS_H */
433
434 /* Fall back to plain stream-based I/O. */
435 #ifndef WIN32
436 /* Wait for input on termin. This code is based on
437 apr_wait_for_io_or_timeout().
438 Note that this will return an EINTR on a signal. */
439 {
440 apr_pollfd_t pollset;
441 int n;
442
443 pollset.desc_type = APR_POLL_FILE;
444 pollset.desc.f = terminal->infd;
445 pollset.p = pool;
446 pollset.reqevents = APR_POLLIN;
447
448 status = apr_poll(&pollset, 1, &n, -1);
449
450 if (n == 1 && pollset.rtnevents & APR_POLLIN)
451 status = APR_SUCCESS;
452 }
453 #endif /* !WIN32 */
454
455 if (!status)
456 status = apr_file_getc(&ch, terminal->infd);
457 if (APR_STATUS_IS_EINTR(status))
458 {
459 *code = TERMINAL_NONE;
460 return SVN_NO_ERROR;
461 }
462 else if (APR_STATUS_IS_EOF(status))
463 {
464 *code = TERMINAL_EOF;
465 return SVN_NO_ERROR;
466 }
467 else if (status)
468 return svn_error_wrap_apr(status, _("Can't read from terminal"));
469
470 *code = (int)(unsigned char)ch;
471 return SVN_NO_ERROR;
472 }
473
474
475 /* Set @a *result to the result of prompting the user with @a
476 * prompt_msg. Use @ *pb to get the cancel_func and cancel_baton.
477 * Do not call the cancel_func if @a *pb is NULL.
478 * Allocate @a *result in @a pool.
479 *
480 * If @a hide is true, then try to avoid displaying the user's input.
481 */
482 static svn_error_t *
prompt(const char ** result,const char * prompt_msg,svn_boolean_t hide,svn_cmdline_prompt_baton2_t * pb,apr_pool_t * pool)483 prompt(const char **result,
484 const char *prompt_msg,
485 svn_boolean_t hide,
486 svn_cmdline_prompt_baton2_t *pb,
487 apr_pool_t *pool)
488 {
489 /* XXX: If this functions ever starts using members of *pb
490 * which were not included in svn_cmdline_prompt_baton_t,
491 * we need to update svn_cmdline_prompt_user2 and its callers. */
492
493 svn_boolean_t saw_first_half_of_eol = FALSE;
494 svn_stringbuf_t *strbuf = svn_stringbuf_create_empty(pool);
495 terminal_handle_t *terminal;
496 int code;
497 char c;
498
499 SVN_ERR(terminal_open(&terminal, hide, pool));
500 SVN_ERR(terminal_puts(prompt_msg, terminal, pool));
501
502 while (1)
503 {
504 SVN_ERR(terminal_getc(&code, terminal, (strbuf->len > 0), pool));
505
506 /* Check for cancellation after a character has been read, some
507 input processing modes may eat ^C and we'll only notice a
508 cancellation signal after characters have been read --
509 sometimes even after a newline. */
510 if (pb)
511 SVN_ERR(pb->cancel_func(pb->cancel_baton));
512
513 switch (code)
514 {
515 case TERMINAL_NONE:
516 /* Nothing useful happened; retry. */
517 continue;
518
519 case TERMINAL_DEL:
520 /* Delete the last input character. terminal_getc takes care
521 of erasing the feedback from the terminal, if applicable. */
522 svn_stringbuf_chop(strbuf, 1);
523 continue;
524
525 case TERMINAL_EOL:
526 /* End-of-line means end of input. Trick the EOL-detection code
527 below to stop reading. */
528 saw_first_half_of_eol = TRUE;
529 c = APR_EOL_STR[1]; /* Could be \0 but still stops reading. */
530 break;
531
532 case TERMINAL_EOF:
533 return svn_error_create(
534 APR_EOF,
535 terminal_close(terminal),
536 _("End of file while reading from terminal"));
537
538 default:
539 /* Convert the returned code back to the character. */
540 c = (char)code;
541 }
542
543 if (saw_first_half_of_eol)
544 {
545 if (c == APR_EOL_STR[1])
546 break;
547 else
548 saw_first_half_of_eol = FALSE;
549 }
550 else if (c == APR_EOL_STR[0])
551 {
552 /* GCC might complain here: "warning: will never be executed"
553 * That's fine. This is a compile-time check for "\r\n\0" */
554 if (sizeof(APR_EOL_STR) == 3)
555 {
556 saw_first_half_of_eol = TRUE;
557 continue;
558 }
559 else if (sizeof(APR_EOL_STR) == 2)
560 break;
561 else
562 /* ### APR_EOL_STR holds more than two chars? Who
563 ever heard of such a thing? */
564 SVN_ERR_MALFUNCTION();
565 }
566
567 svn_stringbuf_appendbyte(strbuf, c);
568 }
569
570 if (terminal->noecho)
571 {
572 /* If terminal echo was turned off, make sure future output
573 to the terminal starts on a new line, as expected. */
574 SVN_ERR(terminal_puts(APR_EOL_STR, terminal, pool));
575 }
576 SVN_ERR(terminal_close(terminal));
577
578 return svn_cmdline_cstring_to_utf8(result, strbuf->data, pool);
579 }
580
581
582
583 /** Prompt functions for auth providers. **/
584
585 /* Helper function for auth provider prompters: mention the
586 * authentication @a realm on stderr, in a manner appropriate for
587 * preceding a prompt; or if @a realm is null, then do nothing.
588 */
589 static svn_error_t *
maybe_print_realm(const char * realm,apr_pool_t * pool)590 maybe_print_realm(const char *realm, apr_pool_t *pool)
591 {
592 if (realm)
593 {
594 terminal_handle_t *terminal;
595 SVN_ERR(terminal_open(&terminal, FALSE, pool));
596 SVN_ERR(terminal_puts(
597 apr_psprintf(pool,
598 _("Authentication realm: %s\n"), realm),
599 terminal, pool));
600 SVN_ERR(terminal_close(terminal));
601 }
602
603 return SVN_NO_ERROR;
604 }
605
606
607 /* This implements 'svn_auth_simple_prompt_func_t'. */
608 svn_error_t *
svn_cmdline_auth_simple_prompt(svn_auth_cred_simple_t ** cred_p,void * baton,const char * realm,const char * username,svn_boolean_t may_save,apr_pool_t * pool)609 svn_cmdline_auth_simple_prompt(svn_auth_cred_simple_t **cred_p,
610 void *baton,
611 const char *realm,
612 const char *username,
613 svn_boolean_t may_save,
614 apr_pool_t *pool)
615 {
616 svn_auth_cred_simple_t *ret = apr_pcalloc(pool, sizeof(*ret));
617 const char *pass_prompt;
618 svn_cmdline_prompt_baton2_t *pb = baton;
619
620 SVN_ERR(maybe_print_realm(realm, pool));
621
622 if (username)
623 ret->username = apr_pstrdup(pool, username);
624 else
625 SVN_ERR(prompt(&(ret->username), _("Username: "), FALSE, pb, pool));
626
627 pass_prompt = apr_psprintf(pool, _("Password for '%s': "), ret->username);
628 SVN_ERR(prompt(&(ret->password), pass_prompt, TRUE, pb, pool));
629 ret->may_save = may_save;
630 *cred_p = ret;
631 return SVN_NO_ERROR;
632 }
633
634
635 /* This implements 'svn_auth_username_prompt_func_t'. */
636 svn_error_t *
svn_cmdline_auth_username_prompt(svn_auth_cred_username_t ** cred_p,void * baton,const char * realm,svn_boolean_t may_save,apr_pool_t * pool)637 svn_cmdline_auth_username_prompt(svn_auth_cred_username_t **cred_p,
638 void *baton,
639 const char *realm,
640 svn_boolean_t may_save,
641 apr_pool_t *pool)
642 {
643 svn_auth_cred_username_t *ret = apr_pcalloc(pool, sizeof(*ret));
644 svn_cmdline_prompt_baton2_t *pb = baton;
645
646 SVN_ERR(maybe_print_realm(realm, pool));
647
648 SVN_ERR(prompt(&(ret->username), _("Username: "), FALSE, pb, pool));
649 ret->may_save = may_save;
650 *cred_p = ret;
651 return SVN_NO_ERROR;
652 }
653
654
655 /* This implements 'svn_auth_ssl_server_trust_prompt_func_t'. */
656 svn_error_t *
svn_cmdline_auth_ssl_server_trust_prompt(svn_auth_cred_ssl_server_trust_t ** cred_p,void * baton,const char * realm,apr_uint32_t failures,const svn_auth_ssl_server_cert_info_t * cert_info,svn_boolean_t may_save,apr_pool_t * pool)657 svn_cmdline_auth_ssl_server_trust_prompt
658 (svn_auth_cred_ssl_server_trust_t **cred_p,
659 void *baton,
660 const char *realm,
661 apr_uint32_t failures,
662 const svn_auth_ssl_server_cert_info_t *cert_info,
663 svn_boolean_t may_save,
664 apr_pool_t *pool)
665 {
666 const char *choice;
667 svn_stringbuf_t *msg;
668 svn_cmdline_prompt_baton2_t *pb = baton;
669 svn_stringbuf_t *buf = svn_stringbuf_createf
670 (pool, _("Error validating server certificate for '%s':\n"), realm);
671
672 if (failures & SVN_AUTH_SSL_UNKNOWNCA)
673 {
674 svn_stringbuf_appendcstr
675 (buf,
676 _(" - The certificate is not issued by a trusted authority. Use the\n"
677 " fingerprint to validate the certificate manually!\n"));
678 }
679
680 if (failures & SVN_AUTH_SSL_CNMISMATCH)
681 {
682 svn_stringbuf_appendcstr
683 (buf, _(" - The certificate hostname does not match.\n"));
684 }
685
686 if (failures & SVN_AUTH_SSL_NOTYETVALID)
687 {
688 svn_stringbuf_appendcstr
689 (buf, _(" - The certificate is not yet valid.\n"));
690 }
691
692 if (failures & SVN_AUTH_SSL_EXPIRED)
693 {
694 svn_stringbuf_appendcstr
695 (buf, _(" - The certificate has expired.\n"));
696 }
697
698 if (failures & SVN_AUTH_SSL_OTHER)
699 {
700 svn_stringbuf_appendcstr
701 (buf, _(" - The certificate has an unknown error.\n"));
702 }
703
704 msg = svn_stringbuf_createf
705 (pool,
706 _("Certificate information:\n"
707 " - Hostname: %s\n"
708 " - Valid: from %s until %s\n"
709 " - Issuer: %s\n"
710 " - Fingerprint: %s\n"),
711 cert_info->hostname,
712 cert_info->valid_from,
713 cert_info->valid_until,
714 cert_info->issuer_dname,
715 cert_info->fingerprint);
716 svn_stringbuf_appendstr(buf, msg);
717
718 if (may_save)
719 {
720 svn_stringbuf_appendcstr
721 (buf, _("(R)eject, accept (t)emporarily or accept (p)ermanently? "));
722 }
723 else
724 {
725 svn_stringbuf_appendcstr(buf, _("(R)eject or accept (t)emporarily? "));
726 }
727 SVN_ERR(prompt(&choice, buf->data, FALSE, pb, pool));
728
729 if (choice[0] == 't' || choice[0] == 'T')
730 {
731 *cred_p = apr_pcalloc(pool, sizeof(**cred_p));
732 (*cred_p)->may_save = FALSE;
733 (*cred_p)->accepted_failures = failures;
734 }
735 else if (may_save && (choice[0] == 'p' || choice[0] == 'P'))
736 {
737 *cred_p = apr_pcalloc(pool, sizeof(**cred_p));
738 (*cred_p)->may_save = TRUE;
739 (*cred_p)->accepted_failures = failures;
740 }
741 else
742 {
743 *cred_p = NULL;
744 }
745
746 return SVN_NO_ERROR;
747 }
748
749
750 /* This implements 'svn_auth_ssl_client_cert_prompt_func_t'. */
751 svn_error_t *
svn_cmdline_auth_ssl_client_cert_prompt(svn_auth_cred_ssl_client_cert_t ** cred_p,void * baton,const char * realm,svn_boolean_t may_save,apr_pool_t * pool)752 svn_cmdline_auth_ssl_client_cert_prompt
753 (svn_auth_cred_ssl_client_cert_t **cred_p,
754 void *baton,
755 const char *realm,
756 svn_boolean_t may_save,
757 apr_pool_t *pool)
758 {
759 svn_auth_cred_ssl_client_cert_t *cred = NULL;
760 const char *cert_file = NULL;
761 const char *abs_cert_file = NULL;
762 svn_cmdline_prompt_baton2_t *pb = baton;
763
764 SVN_ERR(maybe_print_realm(realm, pool));
765 SVN_ERR(prompt(&cert_file, _("Client certificate filename: "),
766 FALSE, pb, pool));
767 SVN_ERR(svn_dirent_get_absolute(&abs_cert_file, cert_file, pool));
768
769 cred = apr_palloc(pool, sizeof(*cred));
770 cred->cert_file = abs_cert_file;
771 cred->may_save = may_save;
772 *cred_p = cred;
773
774 return SVN_NO_ERROR;
775 }
776
777
778 /* This implements 'svn_auth_ssl_client_cert_pw_prompt_func_t'. */
779 svn_error_t *
svn_cmdline_auth_ssl_client_cert_pw_prompt(svn_auth_cred_ssl_client_cert_pw_t ** cred_p,void * baton,const char * realm,svn_boolean_t may_save,apr_pool_t * pool)780 svn_cmdline_auth_ssl_client_cert_pw_prompt
781 (svn_auth_cred_ssl_client_cert_pw_t **cred_p,
782 void *baton,
783 const char *realm,
784 svn_boolean_t may_save,
785 apr_pool_t *pool)
786 {
787 svn_auth_cred_ssl_client_cert_pw_t *cred = NULL;
788 const char *result;
789 const char *text = apr_psprintf(pool, _("Passphrase for '%s': "), realm);
790 svn_cmdline_prompt_baton2_t *pb = baton;
791
792 SVN_ERR(prompt(&result, text, TRUE, pb, pool));
793
794 cred = apr_pcalloc(pool, sizeof(*cred));
795 cred->password = result;
796 cred->may_save = may_save;
797 *cred_p = cred;
798
799 return SVN_NO_ERROR;
800 }
801
802 /* This is a helper for plaintext prompt functions. */
803 static svn_error_t *
plaintext_prompt_helper(svn_boolean_t * may_save_plaintext,const char * realmstring,const char * prompt_string,const char * prompt_text,void * baton,apr_pool_t * pool)804 plaintext_prompt_helper(svn_boolean_t *may_save_plaintext,
805 const char *realmstring,
806 const char *prompt_string,
807 const char *prompt_text,
808 void *baton,
809 apr_pool_t *pool)
810 {
811 const char *answer = NULL;
812 svn_boolean_t answered = FALSE;
813 svn_cmdline_prompt_baton2_t *pb = baton;
814 const char *config_path = NULL;
815 terminal_handle_t *terminal;
816
817 *may_save_plaintext = FALSE; /* de facto API promise */
818
819 if (pb)
820 SVN_ERR(svn_config_get_user_config_path(&config_path, pb->config_dir,
821 SVN_CONFIG_CATEGORY_SERVERS, pool));
822
823 SVN_ERR(terminal_open(&terminal, FALSE, pool));
824 SVN_ERR(terminal_puts(apr_psprintf(pool, prompt_text,
825 realmstring, config_path),
826 terminal, pool));
827 SVN_ERR(terminal_close(terminal));
828
829 do
830 {
831 SVN_ERR(prompt(&answer, prompt_string, FALSE, pb, pool));
832 if (apr_strnatcasecmp(answer, _("yes")) == 0 ||
833 apr_strnatcasecmp(answer, _("y")) == 0)
834 {
835 *may_save_plaintext = TRUE;
836 answered = TRUE;
837 }
838 else if (apr_strnatcasecmp(answer, _("no")) == 0 ||
839 apr_strnatcasecmp(answer, _("n")) == 0)
840 {
841 *may_save_plaintext = FALSE;
842 answered = TRUE;
843 }
844 else
845 prompt_string = _("Please type 'yes' or 'no': ");
846 }
847 while (! answered);
848
849 return SVN_NO_ERROR;
850 }
851
852 /* This implements 'svn_auth_plaintext_prompt_func_t'. */
853 svn_error_t *
svn_cmdline_auth_plaintext_prompt(svn_boolean_t * may_save_plaintext,const char * realmstring,void * baton,apr_pool_t * pool)854 svn_cmdline_auth_plaintext_prompt(svn_boolean_t *may_save_plaintext,
855 const char *realmstring,
856 void *baton,
857 apr_pool_t *pool)
858 {
859 const char *prompt_string = _("Store password unencrypted (yes/no)? ");
860 const char *prompt_text =
861 _("\n-----------------------------------------------------------------------"
862 "\nATTENTION! Your password for authentication realm:\n"
863 "\n"
864 " %s\n"
865 "\n"
866 "can only be stored to disk unencrypted! You are advised to configure\n"
867 "your system so that Subversion can store passwords encrypted, if\n"
868 "possible. See the documentation for details.\n"
869 "\n"
870 "You can avoid future appearances of this warning by setting the value\n"
871 "of the 'store-plaintext-passwords' option to either 'yes' or 'no' in\n"
872 "'%s'.\n"
873 "-----------------------------------------------------------------------\n"
874 );
875
876 return plaintext_prompt_helper(may_save_plaintext, realmstring,
877 prompt_string, prompt_text, baton,
878 pool);
879 }
880
881 /* This implements 'svn_auth_plaintext_passphrase_prompt_func_t'. */
882 svn_error_t *
svn_cmdline_auth_plaintext_passphrase_prompt(svn_boolean_t * may_save_plaintext,const char * realmstring,void * baton,apr_pool_t * pool)883 svn_cmdline_auth_plaintext_passphrase_prompt(svn_boolean_t *may_save_plaintext,
884 const char *realmstring,
885 void *baton,
886 apr_pool_t *pool)
887 {
888 const char *prompt_string = _("Store passphrase unencrypted (yes/no)? ");
889 const char *prompt_text =
890 _("\n-----------------------------------------------------------------------\n"
891 "ATTENTION! Your passphrase for client certificate:\n"
892 "\n"
893 " %s\n"
894 "\n"
895 "can only be stored to disk unencrypted! You are advised to configure\n"
896 "your system so that Subversion can store passphrase encrypted, if\n"
897 "possible. See the documentation for details.\n"
898 "\n"
899 "You can avoid future appearances of this warning by setting the value\n"
900 "of the 'store-ssl-client-cert-pp-plaintext' option to either 'yes' or\n"
901 "'no' in '%s'.\n"
902 "-----------------------------------------------------------------------\n"
903 );
904
905 return plaintext_prompt_helper(may_save_plaintext, realmstring,
906 prompt_string, prompt_text, baton,
907 pool);
908 }
909
910
911 /** Generic prompting. **/
912
913 svn_error_t *
svn_cmdline_prompt_user2(const char ** result,const char * prompt_str,svn_cmdline_prompt_baton_t * baton,apr_pool_t * pool)914 svn_cmdline_prompt_user2(const char **result,
915 const char *prompt_str,
916 svn_cmdline_prompt_baton_t *baton,
917 apr_pool_t *pool)
918 {
919 /* XXX: We know prompt doesn't use the new members
920 * of svn_cmdline_prompt_baton2_t. */
921 return prompt(result, prompt_str, FALSE /* don't hide input */,
922 (svn_cmdline_prompt_baton2_t *)baton, pool);
923 }
924
925 /* This implements 'svn_auth_gnome_keyring_unlock_prompt_func_t'. */
926 svn_error_t *
svn_cmdline__auth_gnome_keyring_unlock_prompt(char ** keyring_password,const char * keyring_name,void * baton,apr_pool_t * pool)927 svn_cmdline__auth_gnome_keyring_unlock_prompt(char **keyring_password,
928 const char *keyring_name,
929 void *baton,
930 apr_pool_t *pool)
931 {
932 const char *password;
933 const char *pass_prompt;
934 svn_cmdline_prompt_baton2_t *pb = baton;
935
936 pass_prompt = apr_psprintf(pool, _("Password for '%s' GNOME keyring: "),
937 keyring_name);
938 SVN_ERR(prompt(&password, pass_prompt, TRUE, pb, pool));
939 *keyring_password = apr_pstrdup(pool, password);
940 return SVN_NO_ERROR;
941 }
942