comparison pwm.c @ 18:1e39a251cbe9

Use libtecla for interactive input
author Guido Berhoerster <guido+pwm@berhoerster.name>
date Thu, 24 Aug 2017 13:10:56 +0200
parents a07665727c19
children 5c6155c8e9b6
comparison
equal deleted inserted replaced
17:a08ef0674d8e 18:1e39a251cbe9
25 25
26 #ifdef HAVE_ERR_H 26 #ifdef HAVE_ERR_H
27 #include <err.h> 27 #include <err.h>
28 #endif /* HAVE_ERR_H */ 28 #endif /* HAVE_ERR_H */
29 #include <errno.h> 29 #include <errno.h>
30 #include <fcntl.h>
30 #include <langinfo.h> 31 #include <langinfo.h>
31 #include <locale.h> 32 #include <locale.h>
32 #include <pwd.h> 33 #include <pwd.h>
33 #ifdef HAVE_READPASSPHRASE_H
34 #include <readpassphrase.h>
35 #endif /* READPASSPHRASE_H */
36 #include <stdlib.h> 34 #include <stdlib.h>
37 #include <stdio.h> 35 #include <stdio.h>
38 #include <string.h> 36 #include <string.h>
39 #include <pwd.h> 37 #include <pwd.h>
40 #include <sys/stat.h> 38 #include <sys/stat.h>
45 #include "cmd.h" 43 #include "cmd.h"
46 #include "pwfile.h" 44 #include "pwfile.h"
47 #include "tok.h" 45 #include "tok.h"
48 #include "util.h" 46 #include "util.h"
49 47
50 #ifndef PWM_LINE_MAX 48 #ifndef PWM_HISTORY_ENTRIES_MAX
51 #define PWM_LINE_MAX 16384 49 #define PWM_HISTORY_ENTRIES_MAX 1024
52 #endif /* !PWM_LINE_MAX */ 50 #endif /* !PWM_HISTORY_MAX */
51
52 #ifndef PWM_HISTORY_LINES_MAX
53 #define PWM_HISTORY_LINES_MAX 256
54 #endif /* !PWM_HISTORY_LINES_MAX */
55
56 #ifndef PWM_HISTORY_MAX
57 #define PWM_HISTORY_MAX (PWM_HISTORY_LINES_MAX * PWM_LINE_MAX)
58 #endif /* !PWM_HISTORY_MAX */
53 59
54 static void 60 static void
55 usage(void) 61 usage(void)
56 { 62 {
57 fprintf(stderr, "usage: %s [-P file] [filename]\n", getprogname()); 63 fprintf(stderr, "usage: %s [-P file] [filename]\n", getprogname());
74 ctx->errmsg = NULL; 80 ctx->errmsg = NULL;
75 } 81 }
76 } 82 }
77 83
78 static int 84 static int
85 complete_nothing(WordCompletion *cpl, void *data, const char *line,
86 int word_end)
87 {
88 return (0);
89 }
90
91 static int
79 run_input_loop(struct pwm_ctx *ctx, int is_interactive) 92 run_input_loop(struct pwm_ctx *ctx, int is_interactive)
80 { 93 {
81 int retval = -1; 94 int retval = -1;
95 char prompt[8 + 2 + 1];
96 GetLine *gl = NULL;
82 char buf[PWM_LINE_MAX]; 97 char buf[PWM_LINE_MAX];
98 char *line = buf;
83 int c; 99 int c;
84 int argc = 0; 100 int argc = 0;
85 char **argv = NULL; 101 char **argv = NULL;
86 struct cmd *cmd; 102 struct cmd *cmd;
87 int i; 103 int i;
88 104
105 snprintf(prompt, sizeof (prompt), "%.*s> ", 8, getprogname());
106
107 /* initialize libtecla */
108 gl = new_GetLine(PWM_LINE_MAX, PWM_HISTORY_MAX);
109 if (gl == NULL) {
110 err(1, "new_GetLine");
111 }
112 gl_limit_history(gl, PWM_HISTORY_LINES_MAX);
113 /* disable default filename completion */
114 gl_customize_completion(gl, NULL, complete_nothing);
115
89 for (;;) { 116 for (;;) {
90 cmd = NULL; 117 cmd = NULL;
91 if (fgets(buf, (int)sizeof (buf), stdin) == NULL) { 118 line = gl_get_line(gl, prompt, NULL, -1);
92 if (ferror(stdin)) { 119 if (line == NULL) {
93 /* error */ 120 switch (gl_return_status(gl)) {
94 warn("failed to read command"); 121 case GLR_EOF:
95 goto out;
96 } else if (feof(stdin)) {
97 /* EOF */
98 break; 122 break;
99 } 123 case GLR_ERROR:
100 } 124 warnx("gl_get_line: %s",
101 if ((buf[strlen(buf) - 1] != '\n') && !feof(stdin)) { 125 gl_error_message(gl, NULL, 0));
102 /* line was truncated */ 126 }
103 fprintf(stderr, "line too long\n"); 127 break;
104 if (is_interactive) {
105 /* skip input to next newline */
106 do {
107 errno = 0;
108 c = fgetc(stdin);
109 if ((c == EOF) && (errno != 0)) {
110 warn("failed to read command");
111 goto out;
112 }
113 } while ((c != '\n') && (c != EOF));
114 } else {
115 /* fatal error in non-interactive mode */
116 goto out;
117 }
118 } 128 }
119 129
120 /* tokenize line */ 130 /* tokenize line */
121 switch(tok_tokenize(buf, &argc, &argv)) { 131 switch (tok_tokenize(line, &argc, &argv)) {
122 case TOK_ERR_SYSTEM_ERROR: 132 case TOK_ERR_SYSTEM_ERROR:
123 err(1, "tok_tokenize"); 133 err(1, "tok_tokenize");
124 case TOK_ERR_UNTERMINATED_QUOTE: 134 case TOK_ERR_UNTERMINATED_QUOTE:
125 fprintf(stderr, "unterminated quote\n"); 135 fprintf(stderr, "unterminated quote\n");
126 if (!is_interactive) { 136 if (!is_interactive) {
181 out: 191 out:
182 for (i = 0; i < argc; i++) { 192 for (i = 0; i < argc; i++) {
183 free(argv[i]); 193 free(argv[i]);
184 } 194 }
185 free(argv); 195 free(argv);
196 del_GetLine(gl);
197
198 return (retval);
199 }
200
201 int
202 pwm_read_password(struct pwm_ctx *ctx, int is_new_password)
203 {
204 int retval = -1;
205 GetLine *gl = NULL;
206 char *line;
207 size_t len;
208 char password_buf[sizeof (ctx->password)] = { '\0' };
209
210 /* initialize libtecla */
211 gl = new_GetLine(sizeof (password_buf) - 1, 0);
212 if (gl == NULL) {
213 err(1, "new_GetLine");
214 }
215 /* disable default filename completion */
216 gl_customize_completion(gl, NULL, complete_nothing);
217 gl_echo_mode(gl, 0);
218
219 line = gl_get_line(gl, is_new_password ? "New password: " :
220 "Password: ", NULL, -1);
221 putchar('\n');
222 if (line == NULL) {
223 if (gl_return_status(gl) == GLR_ERROR) {
224 errx(1, "gl_get_line: %s", gl_error_message(gl, NULL,
225 0));
226 }
227 goto out;
228 }
229 len = strlen(line);
230 if ((len > 0) && (line[len - 1] == '\n')) {
231 line[--len] = '\0';
232 }
233 if (len == 0) {
234 fprintf(stderr, "password must not be empty\n");
235 goto out;
236 }
237 strcpy(password_buf, line);
238
239 /* confirm the entered password entered */
240 if (is_new_password) {
241 line = gl_get_line(gl, "Confirm password: ", NULL, -1);
242 putchar('\n');
243 if (line == NULL) {
244 if (gl_return_status(gl) == GLR_ERROR) {
245 errx(1, "gl_get_line: %s", gl_error_message(gl,
246 NULL, 0));
247 }
248 goto out;
249 }
250 len = strlen(line);
251 if ((len > 0) && (line[len - 1] == '\n')) {
252 line[--len] = '\0';
253 }
254 if (strcmp(password_buf, line) != 0) {
255 fprintf(stderr, "passwords do not match\n");
256 goto out;
257 }
258 }
259
260 strcpy(ctx->password, password_buf);
261 retval = 0;
262
263 out:
264 del_GetLine(gl);
186 265
187 return (retval); 266 return (retval);
188 } 267 }
189 268
190 static int 269 static int
251 const char *master_password_filename = NULL; 330 const char *master_password_filename = NULL;
252 struct pwm_ctx ctx = { 0 }; 331 struct pwm_ctx ctx = { 0 };
253 struct passwd *passwd; 332 struct passwd *passwd;
254 char *pwm_dirname = NULL; 333 char *pwm_dirname = NULL;
255 FILE *fp = NULL; 334 FILE *fp = NULL;
256 char password_buf[PWS3_MAX_PASSWORD_LEN + 1];
257 335
258 setprogname(argv[0]); 336 setprogname(argv[0]);
259 337
260 /* set up locale and check for UTF-8 codeset */ 338 /* set up locale and check for UTF-8 codeset */
261 locale = setlocale(LC_ALL, ""); 339 locale = setlocale(LC_ALL, "");
341 if (master_password_filename != NULL) { 419 if (master_password_filename != NULL) {
342 if (read_password_from_file(master_password_filename, 420 if (read_password_from_file(master_password_filename,
343 ctx.password, sizeof (ctx.password)) != 0) { 421 ctx.password, sizeof (ctx.password)) != 0) {
344 goto out; 422 goto out;
345 } 423 }
346 } else { 424 } else if (pwm_read_password(&ctx, (fp == NULL)) != 0) {
347 if (readpassphrase("Enter password: ", ctx.password, 425 goto out;
348 sizeof (ctx.password), RPP_ECHO_OFF | RPP_REQUIRE_TTY) ==
349 NULL) {
350 err(1, "readpassphrase");
351 }
352 if (ctx.password[0] == '\0') {
353 fprintf(stderr, "password must not be empty\n");
354 goto out;
355 }
356 if (fp == NULL) {
357 if (readpassphrase("Confirm password: ", password_buf,
358 sizeof (password_buf),
359 RPP_ECHO_OFF | RPP_REQUIRE_TTY) == NULL) {
360 err(1, "readpassphrase");
361 }
362 if (strcmp(ctx.password, password_buf) != 0) {
363 fprintf(stderr, "passwords do not match\n");
364 goto out;
365 }
366 }
367 } 426 }
368 if (fp != NULL) { 427 if (fp != NULL) {
369 if (pwfile_read_file(&ctx, fp) != 0) { 428 if (pwfile_read_file(&ctx, fp) != 0) {
370 goto out; 429 goto out;
371 } 430 }