From 9ad0f6812f287856ebb83229fdae22b7af8ab0ea Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Tue, 19 Jun 2001 15:52:00 +0000 Subject: [PATCH] Enhance the user interface with better support for dialog box prompting, application-defined prompts, the possibility to use defaults (for example default passwords from somewhere else) and interrupts/cancelations. --- CHANGES | 6 ++ crypto/ui/ui.h | 109 +++++++++++++++++++++++++------- crypto/ui/ui_lib.c | 140 ++++++++++++++++++++++++++++++++--------- crypto/ui/ui_locl.h | 36 ++++++++--- crypto/ui/ui_openssl.c | 73 +++++++++++---------- 5 files changed, 269 insertions(+), 95 deletions(-) diff --git a/CHANGES b/CHANGES index cf79a8ea10..ef113ed413 100644 --- a/CHANGES +++ b/CHANGES @@ -11,6 +11,12 @@ *) applies to 0.9.6a (/0.9.6b) and 0.9.7 +) applies to 0.9.7 only + +) Enhance the general user interface with mechanisms to better support + dialog box interfaces, application-defined prompts, the possibility + to use defaults (for example default passwords from somewhere else) + and interrupts/cancelations. + [Richard Levitte] + *) Don't change *pointer in CRYPTO_add_lock() is add_lock_callback is used: it isn't thread safe and the add_lock_callback should handle that itself. diff --git a/crypto/ui/ui.h b/crypto/ui/ui.h index 452d9dcbfc..5dda8aba0f 100644 --- a/crypto/ui/ui.h +++ b/crypto/ui/ui.h @@ -80,9 +80,10 @@ typedef struct ui_st UI; typedef struct ui_method_st UI_METHOD; -/* All the following functions return -1 or NULL on error. When everything is - fine, they return 0, a positive value or a non-NULL pointer, all depending - on their purpose. */ +/* All the following functions return -1 or NULL on error and in some cases + (UI_process()) -2 if interrupted or in some other way cancelled. + When everything is fine, they return 0, a positive value or a non-NULL + pointer, all depending on their purpose. */ /* Creators and destructor. */ UI *UI_new(void); @@ -108,7 +109,7 @@ void UI_free(UI *ui); moment. All of the functions in this group take a UI and a string. The input and - verify addition functions also take an echo flag, a buffer for the result + verify addition functions also take a flag argument, a buffer for the result to end up with, a minimum input size and a maximum input size (the result buffer MUST be large enough to be able to contain the maximum number of characters). Additionally, the verify addition functions takes another @@ -116,19 +117,62 @@ void UI_free(UI *ui); On success, the all return an index of the added information. That index is usefull when retrieving results with UI_get0_result(). */ -int UI_add_input_string(UI *ui, const char *prompt, int echo_p, +int UI_add_input_string(UI *ui, const char *prompt, int flags, char *result_buf, int minsize, int maxsize); -int UI_dup_input_string(UI *ui, const char *prompt, int echo_p, +int UI_dup_input_string(UI *ui, const char *prompt, int flags, char *result_buf, int minsize, int maxsize); -int UI_add_verify_string(UI *ui, const char *prompt, int echo_p, +int UI_add_verify_string(UI *ui, const char *prompt, int flags, char *result_buf, int minsize, int maxsize, const char *test_buf); -int UI_dup_verify_string(UI *ui, const char *prompt, int echo_p, +int UI_dup_verify_string(UI *ui, const char *prompt, int flags, char *result_buf, int minsize, int maxsize, const char *test_buf); int UI_add_info_string(UI *ui, const char *text); int UI_dup_info_string(UI *ui, const char *text); int UI_add_error_string(UI *ui, const char *text); int UI_dup_error_string(UI *ui, const char *text); +/* These are the possible flags. They can be or'ed together. */ +/* Use to have echoing of input */ +#define UI_INPUT_FLAG_ECHO 0x01 +/* Use a default answer. Where that answer is found is completely up + to the application, it might for example be in the user data set + with UI_add_user_data(). It is not recommended to have more than + one input in each UI being marked with this flag, or the application + might get confused. */ +#define UI_INPUT_FLAG_DEFAULT 0x02 + +/* The user of these routines may want to define flags of their own. The core + UI won't look at those, but will pass them on to the method routines. They + must use higher bits so they don't get confused with the UI bits above. + UI_INPUT_FLAG_USER_BASE tells which is the lowest bit to use. A good + example of use is this: + + #define MY_UI_FLAG1 (0x01 << UI_INPUT_FLAG_USER_BASE) + +*/ +#define UI_INPUT_FLAG_USER_BASE 16 + + +/* The following function helps construct a prompt. object_desc is a + textual short description of the object, for example "pass phrase", + and object_name is the name of the object (might be a card name or + a file name. + The returned string shall always be allocated on the heap with + OPENSSL_malloc(), and need to be free'd with OPENSSL_free(). + + If the ui_method doesn't contain a pointer to a user-defined prompt + constructor, a default string is built, looking like this: + + "Enter {object_desc} for {object_name}:" + + So, if object_desc has the value "pass phrase" and object_name has + the value "foo.key", the resulting string is: + + "Enter pass phrase for foo.key:" +*/ +char *UI_construct_prompt(UI *ui_method, + const char *object_desc, const char *object_name); + + /* The following function is used to store a pointer to user-specific data. Any previous such pointer will be returned and replaced. @@ -175,6 +219,9 @@ UI_METHOD *UI_OpenSSL(void); a writer This function is called to write a given string, maybe to the tty, maybe as a field label in a window. + a flusher This function is called to flush everything that + has been output so far. It can be used to actually + display a dialog box after it has been built. a reader This function is called to read a given prompt, maybe from the tty, maybe from a field in a window. Note that it's called wth all string @@ -183,13 +230,27 @@ UI_METHOD *UI_OpenSSL(void); a closer This function closes the session, maybe by closing the channel to the tty, or closing the window. + All these functions are expected to return: + + 0 on error. + 1 on success. + -1 on out-of-band events, for example if some prompting has + been canceled (by pressing Ctrl-C, for example). This is + only checked when returned by the flusher or the reader. + The way this is used, the opener is first called, then the writer for all - strings, then the reader for all strings and finally the closer. Note that - if you want to prompt from a terminal or other command line interface, the - best is to have the reader also write the prompts instead of having the - writer do it. + strings, then the flusher, then the reader for all strings and finally the + closer. Note that if you want to prompt from a terminal or other command + line interface, the best is to have the reader also write the prompts + instead of having the writer do it. If you want to prompt from a dialog + box, the writer can be used to build up the contents of the box, and the + flusher to actually display the box and run the event loop until all data + has been given, after which the reader only grabs the given data and puts + them back into the UI strings. + All method functions take a UI as argument. Additionally, the writer and - the reader take a UI_STRING. */ + the reader take a UI_STRING. +*/ /* The UI_STRING type is the data structure that contains all the needed info about a string or a prompt, including test data for a verification prompt. @@ -201,31 +262,33 @@ typedef struct ui_string_st UI_STRING; This is only needed by method authors. */ enum UI_string_types { - UI_NONE=0, - UI_STRING_ECHO, /* Prompt for a string */ - UI_STRING_NOECHO, /* Prompt for a hidden string */ - UI_VERIFY_ECHO, /* Prompt for a string and verify */ - UI_VERIFY_NOECHO, /* Prompt for a hidden string and verify */ - UI_INFO, /* Send info to the user */ - UI_ERROR /* Send an error message to the user */ + UIT_NONE=0, + UIT_PROMPT, /* Prompt for a string */ + UIT_VERIFY, /* Prompt for a string and verify */ + UIT_INFO, /* Send info to the user */ + UIT_ERROR /* Send an error message to the user */ }; /* Create and manipulate methods */ -UI_METHOD *UI_create_method(void); +UI_METHOD *UI_create_method(char *name); int UI_method_set_opener(UI_METHOD *method, int (*opener)(UI *ui)); int UI_method_set_writer(UI_METHOD *method, int (*writer)(UI *ui, UI_STRING *uis)); +int UI_method_set_flusher(UI_METHOD *method, int (*flusher)(UI *ui)); int UI_method_set_reader(UI_METHOD *method, int (*reader)(UI *ui, UI_STRING *uis)); int UI_method_set_closer(UI_METHOD *method, int (*closer)(UI *ui)); int (*UI_method_get_opener(UI_METHOD *method))(UI*); int (*UI_method_get_writer(UI_METHOD *method))(UI*,UI_STRING*); +int (*UI_method_get_flusher(UI_METHOD *method))(UI*); int (*UI_method_get_reader(UI_METHOD *method))(UI*,UI_STRING*); int (*UI_method_get_closer(UI_METHOD *method))(UI*); /* The following functions are helpers for method writers to access relevant data from a UI_STRING. */ -/* Return type type of the UI_STRING */ +/* Return type of the UI_STRING */ enum UI_string_types UI_get_string_type(UI_STRING *uis); +/* Return input flags of the UI_STRING */ +int UI_get_input_flags(UI_STRING *uis); /* Return the actual string to output (the prompt, info or error) */ const char *UI_get0_output_string(UI_STRING *uis); /* Return the result of a prompt */ @@ -237,7 +300,7 @@ int UI_get_result_minsize(UI_STRING *uis); /* Return the required maximum size of the result */ int UI_get_result_maxsize(UI_STRING *uis); /* Set the result of a UI_STRING. */ -int UI_set_result(UI_STRING *uis, char *result); +int UI_set_result(UI_STRING *uis, const char *result); /* BEGIN ERROR CODES */ diff --git a/crypto/ui/ui_lib.c b/crypto/ui/ui_lib.c index 5b6cacf9f8..ffbcb75958 100644 --- a/crypto/ui/ui_lib.c +++ b/crypto/ui/ui_lib.c @@ -131,7 +131,7 @@ static int allocate_string_stack(UI *ui) } static int general_allocate_string(UI *ui, const char *prompt, - int prompt_freeable, enum UI_string_types type, + int prompt_freeable, enum UI_string_types type, int input_flags, char *result_buf, int minsize, int maxsize, const char *test_buf) { int ret=-1; @@ -145,6 +145,7 @@ static int general_allocate_string(UI *ui, const char *prompt, UI_STRING *s=(UI_STRING *)OPENSSL_malloc(sizeof(UI_STRING)); s->out_string=prompt; s->flags=prompt_freeable ? OUT_STRING_FREEABLE : 0; + s->input_flags=input_flags; s->type=type; s->result_buf=result_buf; s->result_minsize=minsize; @@ -157,16 +158,15 @@ static int general_allocate_string(UI *ui, const char *prompt, /* Returns the index to the place in the stack or 0 for error. Uses a direct reference to the prompt. */ -int UI_add_input_string(UI *ui, const char *prompt, int echo_p, +int UI_add_input_string(UI *ui, const char *prompt, int flags, char *result_buf, int minsize, int maxsize) { return general_allocate_string(ui, prompt, 0, - echo_p?UI_STRING_ECHO:UI_STRING_NOECHO, - result_buf, minsize, maxsize, NULL); + UIT_PROMPT, flags, result_buf, minsize, maxsize, NULL); } /* Same as UI_add_input_string(), excepts it takes a copy of the prompt */ -int UI_dup_input_string(UI *ui, const char *prompt, int echo_p, +int UI_dup_input_string(UI *ui, const char *prompt, int flags, char *result_buf, int minsize, int maxsize) { char *prompt_copy=NULL; @@ -182,19 +182,17 @@ int UI_dup_input_string(UI *ui, const char *prompt, int echo_p, } return general_allocate_string(ui, prompt, 1, - echo_p?UI_STRING_ECHO:UI_STRING_NOECHO, - result_buf, minsize, maxsize, NULL); + UIT_PROMPT, flags, result_buf, minsize, maxsize, NULL); } -int UI_add_verify_string(UI *ui, const char *prompt, int echo_p, +int UI_add_verify_string(UI *ui, const char *prompt, int flags, char *result_buf, int minsize, int maxsize, const char *test_buf) { return general_allocate_string(ui, prompt, 0, - echo_p?UI_VERIFY_ECHO:UI_VERIFY_NOECHO, - result_buf, minsize, maxsize, test_buf); + UIT_VERIFY, flags, result_buf, minsize, maxsize, test_buf); } -int UI_dup_verify_string(UI *ui, const char *prompt, int echo_p, +int UI_dup_verify_string(UI *ui, const char *prompt, int flags, char *result_buf, int minsize, int maxsize, const char *test_buf) { char *prompt_copy=NULL; @@ -210,13 +208,13 @@ int UI_dup_verify_string(UI *ui, const char *prompt, int echo_p, } return general_allocate_string(ui, prompt, 1, - echo_p?UI_VERIFY_ECHO:UI_VERIFY_NOECHO, - result_buf, minsize, maxsize, test_buf); + UIT_VERIFY, flags, result_buf, minsize, maxsize, test_buf); } int UI_add_info_string(UI *ui, const char *text) { - return general_allocate_string(ui, text, 0, UI_INFO, NULL, 0, 0, NULL); + return general_allocate_string(ui, text, 0, UIT_INFO, 0, NULL, 0, 0, + NULL); } int UI_dup_info_string(UI *ui, const char *text) @@ -233,12 +231,13 @@ int UI_dup_info_string(UI *ui, const char *text) } } - return general_allocate_string(ui, text, 1, UI_INFO, NULL, 0, 0, NULL); + return general_allocate_string(ui, text, 1, UIT_INFO, 0, NULL, 0, 0, + NULL); } int UI_add_error_string(UI *ui, const char *text) { - return general_allocate_string(ui, text, 0, UI_ERROR, NULL, 0, 0, + return general_allocate_string(ui, text, 0, UIT_ERROR, 0, NULL, 0, 0, NULL); } @@ -255,8 +254,43 @@ int UI_dup_error_string(UI *ui, const char *text) return -1; } } - return general_allocate_string(ui, text_copy, 1, UI_ERROR, NULL, 0, 0, - NULL); + return general_allocate_string(ui, text_copy, 1, UIT_ERROR, 0, NULL, + 0, 0, NULL); + } + +char *UI_construct_prompt(UI *ui, const char *object_desc, + const char *object_name) + { + char *prompt = NULL; + + if (ui->meth->ui_construct_prompt) + prompt = ui->meth->ui_construct_prompt(ui, + object_desc, object_name); + else + { + char prompt1[] = "Enter "; + char prompt2[] = " for "; + char prompt3[] = ":"; + int len = 0; + + if (object_desc == NULL) + return NULL; + len = sizeof(prompt1) - 1 + strlen(object_desc); + if (object_name) + len += sizeof(prompt2) - 1 + strlen(object_name); + len += sizeof(prompt3) - 1; + + prompt = (char *)OPENSSL_malloc(len + 1); + strcpy(prompt, prompt1); + strcat(prompt, object_desc); + if (object_name) + { + strcat(prompt, prompt2); + strcat(prompt, object_name); + } + strcat(prompt, prompt3); + } + return prompt; } void *UI_add_user_data(UI *ui, void *user_data) @@ -304,14 +338,37 @@ int UI_process(UI *ui) } } + if (ui->meth->ui_flush) + switch(ui->meth->ui_flush(ui)) + { + case -1: /* Interrupt/Cancel/something... */ + ok = -2; + goto err; + case 0: /* Errors */ + ok = -1; + goto err; + default: /* Success */ + ok = 0; + break; + } + for(i=0; istrings); i++) { - if (ui->meth->ui_read_string - && !ui->meth->ui_read_string(ui, - sk_UI_STRING_value(ui->strings, i))) + if (ui->meth->ui_read_string) { - ok=-1; - goto err; + switch(ui->meth->ui_read_string(ui, + sk_UI_STRING_value(ui->strings, i))) + { + case -1: /* Interrupt/Cancel/something... */ + ok = -2; + goto err; + case 0: /* Errors */ + ok = -1; + goto err; + default: /* Success */ + ok = 0; + break; + } } } err: @@ -364,9 +421,14 @@ const UI_METHOD *UI_set_method(UI *ui, const UI_METHOD *meth) } -UI_METHOD *UI_create_method(void) +UI_METHOD *UI_create_method(char *name) { - return (UI_METHOD *)OPENSSL_malloc(sizeof(UI_METHOD)); + UI_METHOD *ui_method = (UI_METHOD *)OPENSSL_malloc(sizeof(UI_METHOD)); + + if (ui_method) + memset(ui_method, 0, sizeof(*ui_method)); + ui_method->name = strdup(name); + return ui_method; } int UI_method_set_opener(UI_METHOD *method, int (*opener)(UI *ui)) @@ -391,6 +453,17 @@ int UI_method_set_writer(UI_METHOD *method, int (*writer)(UI *ui, UI_STRING *uis return -1; } +int UI_method_set_flusher(UI_METHOD *method, int (*flusher)(UI *ui)) + { + if (method) + { + method->ui_flush = flusher; + return 0; + } + else + return -1; + } + int UI_method_set_reader(UI_METHOD *method, int (*reader)(UI *ui, UI_STRING *uis)) { if (method) @@ -448,10 +521,17 @@ int (*UI_method_get_closer(UI_METHOD *method))(UI*) enum UI_string_types UI_get_string_type(UI_STRING *uis) { if (!uis) - return UI_NONE; + return UIT_NONE; return uis->type; } +int UI_get_input_flags(UI_STRING *uis) + { + if (!uis) + return 0; + return uis->input_flags; + } + const char *UI_get0_output_string(UI_STRING *uis) { if (!uis) @@ -465,10 +545,8 @@ const char *UI_get0_result_string(UI_STRING *uis) return NULL; switch(uis->type) { - case UI_STRING_ECHO: - case UI_STRING_NOECHO: - case UI_VERIFY_ECHO: - case UI_VERIFY_NOECHO: + case UIT_PROMPT: + case UIT_VERIFY: return uis->result_buf; default: return NULL; @@ -496,7 +574,7 @@ int UI_get_result_maxsize(UI_STRING *uis) return uis->result_maxsize; } -int UI_set_result(UI_STRING *uis, char *result) +int UI_set_result(UI_STRING *uis, const char *result) { int l = strlen(result); diff --git a/crypto/ui/ui_locl.h b/crypto/ui/ui_locl.h index 4adf0d821b..8f48f8e8c1 100644 --- a/crypto/ui/ui_locl.h +++ b/crypto/ui/ui_locl.h @@ -65,22 +65,40 @@ struct ui_method_st { const char *name; - /* All the functions return 1 for success and 0 for failure */ - int (*ui_open_session)(UI *ui); /* Open whatever channel for this, - be it the console, an X window - or whatever. - This function should use the - ex_data structure to save - intermediate data. */ - int (*ui_read_string)(UI *ui, UI_STRING *uis); + /* All the functions return 1 or non-NULL for success and 0 or NULL + for failure */ + + /* Open whatever channel for this, be it the console, an X window + or whatever. + This function should use the ex_data structure to save + intermediate data. */ + int (*ui_open_session)(UI *ui); + int (*ui_write_string)(UI *ui, UI_STRING *uis); + + /* Flush the output. If a GUI dialog box is used, this function can + be used to actually display it. */ + int (*ui_flush)(UI *ui); + + int (*ui_read_string)(UI *ui, UI_STRING *uis); + int (*ui_close_session)(UI *ui); + + /* Construct a prompt in a user-defined manner. object_desc is a + textual short description of the object, for example "pass phrase", + and object_name is the name of the object (might be a card name or + a file name. + The returned string shall always be allocated on the heap with + OPENSSL_malloc(), and need to be free'd with OPENSSL_free(). */ + char *(*ui_construct_prompt)(UI *ui, const char *object_desc, + const char *object_name); }; struct ui_string_st { const char *out_string; /* Input */ enum UI_string_types type; /* Input */ + int input_flags; /* Flags from the user */ /* The following parameters are completely irrelevant for UI_INFO, and can therefore be set to 0 ro NULL */ @@ -95,7 +113,7 @@ struct ui_string_st const char *test_buf; /* Input: test string to verify against */ #define OUT_STRING_FREEABLE 0x01 - int flags; + int flags; /* flags for internal use */ }; struct ui_st diff --git a/crypto/ui/ui_openssl.c b/crypto/ui/ui_openssl.c index bb66291ad5..7c988aa708 100644 --- a/crypto/ui/ui_openssl.c +++ b/crypto/ui/ui_openssl.c @@ -283,6 +283,7 @@ static int noecho_fgets(char *buf, int size, FILE *tty); static int read_string_inner(UI *ui, UI_STRING *uis, int echo); static int read_string(UI *ui, UI_STRING *uis); +static int write_string(UI *ui, UI_STRING *uis); static int open_console(UI *ui); static int echo_console(UI *ui); @@ -293,9 +294,11 @@ static UI_METHOD ui_openssl = { "OpenSSL default user interface", open_console, + write_string, + NULL, /* No flusher is needed for command lines */ read_string, - NULL, /* The reader function writes as well */ close_console, + NULL }; /* The method with all the built-in thingies */ @@ -304,30 +307,41 @@ UI_METHOD *UI_OpenSSL(void) return &ui_openssl; } -static int read_string(UI *ui, UI_STRING *uis) +/* The following function makes sure that info and error strings are printed + before any prompt. */ +static int write_string(UI *ui, UI_STRING *uis) { switch (UI_get_string_type(uis)) { - case UI_VERIFY_NOECHO: - fprintf(tty_out,"Verifying - %s", - UI_get0_output_string(uis)); + case UIT_VERIFY: + case UIT_PROMPT: + break; + default: + fputs(UI_get0_output_string(uis), tty_out); fflush(tty_out); - if (read_string_inner(ui, uis, 0) == 0) - return 0; - if (strcmp(UI_get0_result_string(uis), - UI_get0_test_string(uis)) != 0) - { - fprintf(tty_out,"Verify failure\n"); - fflush(tty_out); - return 0; - } break; - case UI_VERIFY_ECHO: + } + return 1; + } + +static int read_string(UI *ui, UI_STRING *uis) + { + int ok = 0; + + switch (UI_get_string_type(uis)) + { + case UIT_PROMPT: + fputs(UI_get0_output_string(uis), tty_out); + fflush(tty_out); + return read_string_inner(ui, uis, + UI_get_input_flags(uis) & UI_INPUT_FLAG_ECHO); + case UIT_VERIFY: fprintf(tty_out,"Verifying - %s", UI_get0_output_string(uis)); fflush(tty_out); - if (read_string_inner(ui, uis, 1) == 0) - return 0; + if ((ok = read_string_inner(ui, uis, + UI_get_input_flags(uis) & UI_INPUT_FLAG_ECHO)) <= 0) + return ok; if (strcmp(UI_get0_result_string(uis), UI_get0_test_string(uis)) != 0) { @@ -336,17 +350,7 @@ static int read_string(UI *ui, UI_STRING *uis) return 0; } break; - case UI_STRING_NOECHO: - fputs(UI_get0_output_string(uis), tty_out); - fflush(tty_out); - return read_string_inner(ui, uis, 0); - case UI_STRING_ECHO: - fputs(UI_get0_output_string(uis), tty_out); - fflush(tty_out); - return read_string_inner(ui, uis, 1); default: - fputs(UI_get0_output_string(uis), tty_out); - fflush(tty_out); break; } return 1; @@ -372,9 +376,9 @@ static int read_string_inner(UI *ui, UI_STRING *uis, int echo) int maxsize = BUFSIZ-1; #ifndef OPENSSL_SYS_WIN16 - if (setjmp(save)) + if ((ok = setjmp(save))) { - ok=0; + if (ok == 1) ok=0; goto error; } ok=0; @@ -594,10 +598,15 @@ static void popsig(void) static void recsig(int i) { + switch(i) + { + case SIGINT: + longjmp(save,-1); + break; + default: + break; + } longjmp(save,1); -#ifdef LINT - i=i; -#endif } -- 2.25.1