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_READ | APR_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 apr_status_t status;
240 const char *converted;
241
242 err = svn_cmdline_cstring_from_utf8(&converted, string, pool);
243 if (err)
244 {
245 svn_error_clear(err);
246 converted = svn_cmdline_cstring_from_utf8_fuzzy(string, pool);
247 }
248
249 #ifdef WIN32
250 if (!terminal->outfd)
251 {
252 /* See terminal_open; we're using Console I/O. */
253 _cputs(converted);
254 return SVN_NO_ERROR;
255 }
256 #endif
257
258 status = apr_file_write_full(terminal->outfd, converted,
259 strlen(converted), NULL);
260 if (!status)
261 status = apr_file_flush(terminal->outfd);
262 if (status)
263 return svn_error_wrap_apr(status, _("Can't write to terminal"));
264 return SVN_NO_ERROR;
265 }
266
267 /* These codes can be returned from terminal_getc instead of a character. */
268 #define TERMINAL_NONE 0x80000 /* no character read, retry */
269 #define TERMINAL_DEL (TERMINAL_NONE + 1) /* the input was a deleteion */
270 #define TERMINAL_EOL (TERMINAL_NONE + 2) /* end of input/end of line */
271 #define TERMINAL_EOF (TERMINAL_NONE + 3) /* end of file during input */
272
273 /* Helper for terminal_getc: writes CH to OUTFD as a control char. */
274 #ifndef WIN32
275 static void
echo_control_char(char ch,apr_file_t * outfd)276 echo_control_char(char ch, apr_file_t *outfd)
277 {
278 if (svn_ctype_iscntrl(ch))
279 {
280 const char substitute = (ch < 32? '@' + ch : '?');
281 apr_file_putc('^', outfd);
282 apr_file_putc(substitute, outfd);
283 }
284 else if (svn_ctype_isprint(ch))
285 {
286 /* Pass printable characters unchanged. */
287 apr_file_putc(ch, outfd);
288 }
289 else
290 {
291 /* Everything else is strange. */
292 apr_file_putc('^', outfd);
293 apr_file_putc('!', outfd);
294 }
295 }
296 #endif /* WIN32 */
297
298 /* Read one character or control code from TERMINAL, returning it in CODE.
299 if CAN_ERASE and the input was a deletion, emit codes to erase the
300 last character displayed on the terminal.
301 Use POOL for all allocations. */
302 static svn_error_t *
terminal_getc(int * code,terminal_handle_t * terminal,svn_boolean_t can_erase,apr_pool_t * pool)303 terminal_getc(int *code, terminal_handle_t *terminal,
304 svn_boolean_t can_erase, apr_pool_t *pool)
305 {
306 const svn_boolean_t echo = !terminal->noecho;
307 apr_status_t status = APR_SUCCESS;
308 char ch;
309
310 #ifdef WIN32
311 if (!terminal->infd)
312 {
313 /* See terminal_open; we're using Console I/O. */
314
315 /* The following was hoisted from APR's getpass for Windows. */
316 int concode = _getch();
317 switch (concode)
318 {
319 case '\r': /* end-of-line */
320 *code = TERMINAL_EOL;
321 if (echo)
322 _cputs("\r\n");
323 break;
324
325 case EOF: /* end-of-file */
326 case 26: /* Ctrl+Z */
327 *code = TERMINAL_EOF;
328 if (echo)
329 _cputs((concode == EOF ? "[EOF]\r\n" : "^Z\r\n"));
330 break;
331
332 case 3: /* Ctrl+C, Ctrl+Break */
333 /* _getch() bypasses Ctrl+C but not Ctrl+Break detection! */
334 if (echo)
335 _cputs("^C\r\n");
336 return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL);
337
338 case 0: /* Function code prefix */
339 case 0xE0:
340 concode = (concode << 4) | _getch();
341 /* Catch {DELETE}, {<--}, Num{DEL} and Num{<--} */
342 if (concode == 0xE53 || concode == 0xE4B
343 || concode == 0x053 || concode == 0x04B)
344 {
345 *code = TERMINAL_DEL;
346 if (can_erase)
347 _cputs("\b \b");
348 }
349 else
350 {
351 *code = TERMINAL_NONE;
352 _putch('\a');
353 }
354 break;
355
356 case '\b': /* BS */
357 case 127: /* DEL */
358 *code = TERMINAL_DEL;
359 if (can_erase)
360 _cputs("\b \b");
361 break;
362
363 default:
364 if (!apr_iscntrl(concode))
365 {
366 *code = (int)(unsigned char)concode;
367 _putch(echo ? concode : '*');
368 }
369 else
370 {
371 *code = TERMINAL_NONE;
372 _putch('\a');
373 }
374 }
375 return SVN_NO_ERROR;
376 }
377 #elif defined(HAVE_TERMIOS_H)
378 if (terminal->restore_state)
379 {
380 /* We're using a bytewise-immediate termios input */
381 const struct termios *const attr = &terminal->attr;
382
383 status = apr_file_getc(&ch, terminal->infd);
384 if (status)
385 return svn_error_wrap_apr(status, _("Can't read from terminal"));
386
387 if (ch == attr->c_cc[VINTR] || ch == attr->c_cc[VQUIT])
388 {
389 /* Break */
390 echo_control_char(ch, terminal->outfd);
391 return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL);
392 }
393 else if (ch == '\r' || ch == '\n' || ch == attr->c_cc[VEOL])
394 {
395 /* Newline */
396 *code = TERMINAL_EOL;
397 apr_file_putc('\n', terminal->outfd);
398 }
399 else if (ch == '\b' || ch == attr->c_cc[VERASE])
400 {
401 /* Delete */
402 *code = TERMINAL_DEL;
403 if (can_erase)
404 {
405 apr_file_putc('\b', terminal->outfd);
406 apr_file_putc(' ', terminal->outfd);
407 apr_file_putc('\b', terminal->outfd);
408 }
409 }
410 else if (ch == attr->c_cc[VEOF])
411 {
412 /* End of input */
413 *code = TERMINAL_EOF;
414 echo_control_char(ch, terminal->outfd);
415 }
416 else if (ch == attr->c_cc[VSUSP])
417 {
418 /* Suspend */
419 *code = TERMINAL_NONE;
420 kill(0, SIGTSTP);
421 }
422 else if (!apr_iscntrl(ch))
423 {
424 /* Normal character */
425 *code = (int)(unsigned char)ch;
426 apr_file_putc((echo ? ch : '*'), terminal->outfd);
427 }
428 else
429 {
430 /* Ignored character */
431 *code = TERMINAL_NONE;
432 apr_file_putc('\a', terminal->outfd);
433 }
434 return SVN_NO_ERROR;
435 }
436 #endif /* HAVE_TERMIOS_H */
437
438 /* Fall back to plain stream-based I/O. */
439 #ifndef WIN32
440 /* Wait for input on termin. This code is based on
441 apr_wait_for_io_or_timeout().
442 Note that this will return an EINTR on a signal. */
443 {
444 apr_pollfd_t pollset;
445 int n;
446
447 pollset.desc_type = APR_POLL_FILE;
448 pollset.desc.f = terminal->infd;
449 pollset.p = pool;
450 pollset.reqevents = APR_POLLIN;
451
452 status = apr_poll(&pollset, 1, &n, -1);
453
454 if (n == 1 && pollset.rtnevents & APR_POLLIN)
455 status = APR_SUCCESS;
456 }
457 #endif /* !WIN32 */
458
459 if (!status)
460 status = apr_file_getc(&ch, terminal->infd);
461 if (APR_STATUS_IS_EINTR(status))
462 {
463 *code = TERMINAL_NONE;
464 return SVN_NO_ERROR;
465 }
466 else if (APR_STATUS_IS_EOF(status))
467 {
468 *code = TERMINAL_EOF;
469 return SVN_NO_ERROR;
470 }
471 else if (status)
472 return svn_error_wrap_apr(status, _("Can't read from terminal"));
473
474 *code = (int)(unsigned char)ch;
475 return SVN_NO_ERROR;
476 }
477
478
479 /* Set @a *result to the result of prompting the user with @a
480 * prompt_msg. Use @ *pb to get the cancel_func and cancel_baton.
481 * Do not call the cancel_func if @a *pb is NULL.
482 * Allocate @a *result in @a pool.
483 *
484 * If @a hide is true, then try to avoid displaying the user's input.
485 */
486 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)487 prompt(const char **result,
488 const char *prompt_msg,
489 svn_boolean_t hide,
490 svn_cmdline_prompt_baton2_t *pb,
491 apr_pool_t *pool)
492 {
493 /* XXX: If this functions ever starts using members of *pb
494 * which were not included in svn_cmdline_prompt_baton_t,
495 * we need to update svn_cmdline_prompt_user2 and its callers. */
496
497 svn_boolean_t saw_first_half_of_eol = FALSE;
498 svn_stringbuf_t *strbuf = svn_stringbuf_create_empty(pool);
499 terminal_handle_t *terminal;
500 int code;
501 char c;
502
503 SVN_ERR(terminal_open(&terminal, hide, pool));
504 SVN_ERR(terminal_puts(prompt_msg, terminal, pool));
505
506 while (1)
507 {
508 SVN_ERR(terminal_getc(&code, terminal, (strbuf->len > 0), pool));
509
510 /* Check for cancellation after a character has been read, some
511 input processing modes may eat ^C and we'll only notice a
512 cancellation signal after characters have been read --
513 sometimes even after a newline. */
514 if (pb)
515 SVN_ERR(pb->cancel_func(pb->cancel_baton));
516
517 switch (code)
518 {
519 case TERMINAL_NONE:
520 /* Nothing useful happened; retry. */
521 continue;
522
523 case TERMINAL_DEL:
524 /* Delete the last input character. terminal_getc takes care
525 of erasing the feedback from the terminal, if applicable. */
526 svn_stringbuf_chop(strbuf, 1);
527 continue;
528
529 case TERMINAL_EOL:
530 /* End-of-line means end of input. Trick the EOL-detection code
531 below to stop reading. */
532 saw_first_half_of_eol = TRUE;
533 c = APR_EOL_STR[1]; /* Could be \0 but still stops reading. */
534 break;
535
536 case TERMINAL_EOF:
537 return svn_error_create(
538 APR_EOF,
539 terminal_close(terminal),
540 _("End of file while reading from terminal"));
541
542 default:
543 /* Convert the returned code back to the character. */
544 c = (char)code;
545 }
546
547 if (saw_first_half_of_eol)
548 {
549 if (c == APR_EOL_STR[1])
550 break;
551 else
552 saw_first_half_of_eol = FALSE;
553 }
554 else if (c == APR_EOL_STR[0])
555 {
556 /* GCC might complain here: "warning: will never be executed"
557 * That's fine. This is a compile-time check for "\r\n\0" */
558 if (sizeof(APR_EOL_STR) == 3)
559 {
560 saw_first_half_of_eol = TRUE;
561 continue;
562 }
563 else if (sizeof(APR_EOL_STR) == 2)
564 break;
565 else
566 /* ### APR_EOL_STR holds more than two chars? Who
567 ever heard of such a thing? */
568 SVN_ERR_MALFUNCTION();
569 }
570
571 svn_stringbuf_appendbyte(strbuf, c);
572 }
573
574 if (terminal->noecho)
575 {
576 /* If terminal echo was turned off, make sure future output
577 to the terminal starts on a new line, as expected. */
578 SVN_ERR(terminal_puts(APR_EOL_STR, terminal, pool));
579 }
580 SVN_ERR(terminal_close(terminal));
581
582 return svn_cmdline_cstring_to_utf8(result, strbuf->data, pool);
583 }
584
585
586
587 /** Prompt functions for auth providers. **/
588
589 /* Helper function for auth provider prompters: mention the
590 * authentication @a realm on stderr, in a manner appropriate for
591 * preceding a prompt; or if @a realm is null, then do nothing.
592 */
593 static svn_error_t *
maybe_print_realm(const char * realm,apr_pool_t * pool)594 maybe_print_realm(const char *realm, apr_pool_t *pool)
595 {
596 if (realm)
597 {
598 terminal_handle_t *terminal;
599 SVN_ERR(terminal_open(&terminal, FALSE, pool));
600 SVN_ERR(terminal_puts(
601 apr_psprintf(pool,
602 _("Authentication realm: %s\n"), realm),
603 terminal, pool));
604 SVN_ERR(terminal_close(terminal));
605 }
606
607 return SVN_NO_ERROR;
608 }
609
610
611 /* This implements 'svn_auth_simple_prompt_func_t'. */
612 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)613 svn_cmdline_auth_simple_prompt(svn_auth_cred_simple_t **cred_p,
614 void *baton,
615 const char *realm,
616 const char *username,
617 svn_boolean_t may_save,
618 apr_pool_t *pool)
619 {
620 svn_auth_cred_simple_t *ret = apr_pcalloc(pool, sizeof(*ret));
621 const char *pass_prompt;
622 svn_cmdline_prompt_baton2_t *pb = baton;
623
624 SVN_ERR(maybe_print_realm(realm, pool));
625
626 if (username)
627 ret->username = apr_pstrdup(pool, username);
628 else
629 SVN_ERR(prompt(&(ret->username), _("Username: "), FALSE, pb, pool));
630
631 pass_prompt = apr_psprintf(pool, _("Password for '%s': "), ret->username);
632 SVN_ERR(prompt(&(ret->password), pass_prompt, TRUE, pb, pool));
633 ret->may_save = may_save;
634 *cred_p = ret;
635 return SVN_NO_ERROR;
636 }
637
638
639 /* This implements 'svn_auth_username_prompt_func_t'. */
640 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)641 svn_cmdline_auth_username_prompt(svn_auth_cred_username_t **cred_p,
642 void *baton,
643 const char *realm,
644 svn_boolean_t may_save,
645 apr_pool_t *pool)
646 {
647 svn_auth_cred_username_t *ret = apr_pcalloc(pool, sizeof(*ret));
648 svn_cmdline_prompt_baton2_t *pb = baton;
649
650 SVN_ERR(maybe_print_realm(realm, pool));
651
652 SVN_ERR(prompt(&(ret->username), _("Username: "), FALSE, pb, pool));
653 ret->may_save = may_save;
654 *cred_p = ret;
655 return SVN_NO_ERROR;
656 }
657
658
659 /* This implements 'svn_auth_ssl_server_trust_prompt_func_t'. */
660 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)661 svn_cmdline_auth_ssl_server_trust_prompt
662 (svn_auth_cred_ssl_server_trust_t **cred_p,
663 void *baton,
664 const char *realm,
665 apr_uint32_t failures,
666 const svn_auth_ssl_server_cert_info_t *cert_info,
667 svn_boolean_t may_save,
668 apr_pool_t *pool)
669 {
670 const char *choice;
671 svn_stringbuf_t *msg;
672 svn_cmdline_prompt_baton2_t *pb = baton;
673 svn_stringbuf_t *buf = svn_stringbuf_createf
674 (pool, _("Error validating server certificate for '%s':\n"), realm);
675
676 if (failures & SVN_AUTH_SSL_UNKNOWNCA)
677 {
678 svn_stringbuf_appendcstr
679 (buf,
680 _(" - The certificate is not issued by a trusted authority. Use the\n"
681 " fingerprint to validate the certificate manually!\n"));
682 }
683
684 if (failures & SVN_AUTH_SSL_CNMISMATCH)
685 {
686 svn_stringbuf_appendcstr
687 (buf, _(" - The certificate hostname does not match.\n"));
688 }
689
690 if (failures & SVN_AUTH_SSL_NOTYETVALID)
691 {
692 svn_stringbuf_appendcstr
693 (buf, _(" - The certificate is not yet valid.\n"));
694 }
695
696 if (failures & SVN_AUTH_SSL_EXPIRED)
697 {
698 svn_stringbuf_appendcstr
699 (buf, _(" - The certificate has expired.\n"));
700 }
701
702 if (failures & SVN_AUTH_SSL_OTHER)
703 {
704 svn_stringbuf_appendcstr
705 (buf, _(" - The certificate has an unknown error.\n"));
706 }
707
708 msg = svn_stringbuf_createf
709 (pool,
710 _("Certificate information:\n"
711 " - Hostname: %s\n"
712 " - Valid: from %s until %s\n"
713 " - Issuer: %s\n"
714 " - Fingerprint: %s\n"),
715 cert_info->hostname,
716 cert_info->valid_from,
717 cert_info->valid_until,
718 cert_info->issuer_dname,
719 cert_info->fingerprint);
720 svn_stringbuf_appendstr(buf, msg);
721
722 if (may_save)
723 {
724 svn_stringbuf_appendcstr
725 (buf, _("(R)eject, accept (t)emporarily or accept (p)ermanently? "));
726 }
727 else
728 {
729 svn_stringbuf_appendcstr(buf, _("(R)eject or accept (t)emporarily? "));
730 }
731 SVN_ERR(prompt(&choice, buf->data, FALSE, pb, pool));
732
733 if (choice[0] == 't' || choice[0] == 'T')
734 {
735 *cred_p = apr_pcalloc(pool, sizeof(**cred_p));
736 (*cred_p)->may_save = FALSE;
737 (*cred_p)->accepted_failures = failures;
738 }
739 else if (may_save && (choice[0] == 'p' || choice[0] == 'P'))
740 {
741 *cred_p = apr_pcalloc(pool, sizeof(**cred_p));
742 (*cred_p)->may_save = TRUE;
743 (*cred_p)->accepted_failures = failures;
744 }
745 else
746 {
747 *cred_p = NULL;
748 }
749
750 return SVN_NO_ERROR;
751 }
752
753
754 /* This implements 'svn_auth_ssl_client_cert_prompt_func_t'. */
755 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)756 svn_cmdline_auth_ssl_client_cert_prompt
757 (svn_auth_cred_ssl_client_cert_t **cred_p,
758 void *baton,
759 const char *realm,
760 svn_boolean_t may_save,
761 apr_pool_t *pool)
762 {
763 svn_auth_cred_ssl_client_cert_t *cred = NULL;
764 const char *cert_file = NULL;
765 const char *abs_cert_file = NULL;
766 svn_cmdline_prompt_baton2_t *pb = baton;
767
768 SVN_ERR(maybe_print_realm(realm, pool));
769 SVN_ERR(prompt(&cert_file, _("Client certificate filename: "),
770 FALSE, pb, pool));
771 SVN_ERR(svn_dirent_get_absolute(&abs_cert_file, cert_file, pool));
772
773 cred = apr_palloc(pool, sizeof(*cred));
774 cred->cert_file = abs_cert_file;
775 cred->may_save = may_save;
776 *cred_p = cred;
777
778 return SVN_NO_ERROR;
779 }
780
781
782 /* This implements 'svn_auth_ssl_client_cert_pw_prompt_func_t'. */
783 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)784 svn_cmdline_auth_ssl_client_cert_pw_prompt
785 (svn_auth_cred_ssl_client_cert_pw_t **cred_p,
786 void *baton,
787 const char *realm,
788 svn_boolean_t may_save,
789 apr_pool_t *pool)
790 {
791 svn_auth_cred_ssl_client_cert_pw_t *cred = NULL;
792 const char *result;
793 const char *text = apr_psprintf(pool, _("Passphrase for '%s': "), realm);
794 svn_cmdline_prompt_baton2_t *pb = baton;
795
796 SVN_ERR(prompt(&result, text, TRUE, pb, pool));
797
798 cred = apr_pcalloc(pool, sizeof(*cred));
799 cred->password = result;
800 cred->may_save = may_save;
801 *cred_p = cred;
802
803 return SVN_NO_ERROR;
804 }
805
806 /* This is a helper for plaintext prompt functions. */
807 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)808 plaintext_prompt_helper(svn_boolean_t *may_save_plaintext,
809 const char *realmstring,
810 const char *prompt_string,
811 const char *prompt_text,
812 void *baton,
813 apr_pool_t *pool)
814 {
815 const char *answer = NULL;
816 svn_boolean_t answered = FALSE;
817 svn_cmdline_prompt_baton2_t *pb = baton;
818 const char *config_path = NULL;
819 terminal_handle_t *terminal;
820
821 if (pb)
822 SVN_ERR(svn_config_get_user_config_path(&config_path, pb->config_dir,
823 SVN_CONFIG_CATEGORY_SERVERS, pool));
824
825 SVN_ERR(terminal_open(&terminal, FALSE, pool));
826 SVN_ERR(terminal_puts(apr_psprintf(pool, prompt_text,
827 realmstring, config_path),
828 terminal, pool));
829 SVN_ERR(terminal_close(terminal));
830
831 do
832 {
833 svn_error_t *err = prompt(&answer, prompt_string, FALSE, pb, pool);
834 if (err)
835 {
836 if (err->apr_err == SVN_ERR_CANCELLED)
837 {
838 svn_error_clear(err);
839 *may_save_plaintext = FALSE;
840 return SVN_NO_ERROR;
841 }
842 else
843 return err;
844 }
845 if (apr_strnatcasecmp(answer, _("yes")) == 0 ||
846 apr_strnatcasecmp(answer, _("y")) == 0)
847 {
848 *may_save_plaintext = TRUE;
849 answered = TRUE;
850 }
851 else if (apr_strnatcasecmp(answer, _("no")) == 0 ||
852 apr_strnatcasecmp(answer, _("n")) == 0)
853 {
854 *may_save_plaintext = FALSE;
855 answered = TRUE;
856 }
857 else
858 prompt_string = _("Please type 'yes' or 'no': ");
859 }
860 while (! answered);
861
862 return SVN_NO_ERROR;
863 }
864
865 /* This implements 'svn_auth_plaintext_prompt_func_t'. */
866 svn_error_t *
svn_cmdline_auth_plaintext_prompt(svn_boolean_t * may_save_plaintext,const char * realmstring,void * baton,apr_pool_t * pool)867 svn_cmdline_auth_plaintext_prompt(svn_boolean_t *may_save_plaintext,
868 const char *realmstring,
869 void *baton,
870 apr_pool_t *pool)
871 {
872 const char *prompt_string = _("Store password unencrypted (yes/no)? ");
873 const char *prompt_text =
874 _("\n-----------------------------------------------------------------------"
875 "\nATTENTION! Your password for authentication realm:\n"
876 "\n"
877 " %s\n"
878 "\n"
879 "can only be stored to disk unencrypted! You are advised to configure\n"
880 "your system so that Subversion can store passwords encrypted, if\n"
881 "possible. See the documentation for details.\n"
882 "\n"
883 "You can avoid future appearances of this warning by setting the value\n"
884 "of the 'store-plaintext-passwords' option to either 'yes' or 'no' in\n"
885 "'%s'.\n"
886 "-----------------------------------------------------------------------\n"
887 );
888
889 return plaintext_prompt_helper(may_save_plaintext, realmstring,
890 prompt_string, prompt_text, baton,
891 pool);
892 }
893
894 /* This implements 'svn_auth_plaintext_passphrase_prompt_func_t'. */
895 svn_error_t *
svn_cmdline_auth_plaintext_passphrase_prompt(svn_boolean_t * may_save_plaintext,const char * realmstring,void * baton,apr_pool_t * pool)896 svn_cmdline_auth_plaintext_passphrase_prompt(svn_boolean_t *may_save_plaintext,
897 const char *realmstring,
898 void *baton,
899 apr_pool_t *pool)
900 {
901 const char *prompt_string = _("Store passphrase unencrypted (yes/no)? ");
902 const char *prompt_text =
903 _("\n-----------------------------------------------------------------------\n"
904 "ATTENTION! Your passphrase for client certificate:\n"
905 "\n"
906 " %s\n"
907 "\n"
908 "can only be stored to disk unencrypted! You are advised to configure\n"
909 "your system so that Subversion can store passphrase encrypted, if\n"
910 "possible. See the documentation for details.\n"
911 "\n"
912 "You can avoid future appearances of this warning by setting the value\n"
913 "of the 'store-ssl-client-cert-pp-plaintext' option to either 'yes' or\n"
914 "'no' in '%s'.\n"
915 "-----------------------------------------------------------------------\n"
916 );
917
918 return plaintext_prompt_helper(may_save_plaintext, realmstring,
919 prompt_string, prompt_text, baton,
920 pool);
921 }
922
923
924 /** Generic prompting. **/
925
926 svn_error_t *
svn_cmdline_prompt_user2(const char ** result,const char * prompt_str,svn_cmdline_prompt_baton_t * baton,apr_pool_t * pool)927 svn_cmdline_prompt_user2(const char **result,
928 const char *prompt_str,
929 svn_cmdline_prompt_baton_t *baton,
930 apr_pool_t *pool)
931 {
932 /* XXX: We know prompt doesn't use the new members
933 * of svn_cmdline_prompt_baton2_t. */
934 return prompt(result, prompt_str, FALSE /* don't hide input */,
935 (svn_cmdline_prompt_baton2_t *)baton, pool);
936 }
937
938 /* This implements 'svn_auth_gnome_keyring_unlock_prompt_func_t'. */
939 svn_error_t *
svn_cmdline__auth_gnome_keyring_unlock_prompt(char ** keyring_password,const char * keyring_name,void * baton,apr_pool_t * pool)940 svn_cmdline__auth_gnome_keyring_unlock_prompt(char **keyring_password,
941 const char *keyring_name,
942 void *baton,
943 apr_pool_t *pool)
944 {
945 const char *password;
946 const char *pass_prompt;
947 svn_cmdline_prompt_baton2_t *pb = baton;
948
949 pass_prompt = apr_psprintf(pool, _("Password for '%s' GNOME keyring: "),
950 keyring_name);
951 SVN_ERR(prompt(&password, pass_prompt, TRUE, pb, pool));
952 *keyring_password = apr_pstrdup(pool, password);
953 return SVN_NO_ERROR;
954 }
955