Added --modify, --add, --delete and --create options. Allow only
--modify when the user is not the owner of the database. This allows usual users to use popauth the way they use system passwd command, provided popauth is installed setuid.
Showing
1 changed file
with
341 additions
and
34 deletions
... | @@ -17,14 +17,20 @@ | ... | @@ -17,14 +17,20 @@ |
17 | 17 | ||
18 | #include "pop3d.h" | 18 | #include "pop3d.h" |
19 | 19 | ||
20 | static char short_options[] = "f:hlo:v"; | 20 | static char short_options[] = "acdf:hlmo:p:u:v"; |
21 | 21 | ||
22 | static struct option long_options[] = | 22 | static struct option long_options[] = |
23 | { | 23 | { |
24 | { "add", no_argument, 0, 'a' }, | ||
25 | { "create", no_argument, 0, 'c' }, | ||
26 | { "modify", no_argument, 0, 'm' }, | ||
27 | { "delete", no_argument, 0, 'd' }, | ||
24 | { "file", required_argument, 0, 'f' }, | 28 | { "file", required_argument, 0, 'f' }, |
25 | { "help", no_argument, 0, 'h' }, | 29 | { "help", no_argument, 0, 'h' }, |
26 | { "list", no_argument, 0, 'l' }, | 30 | { "list", no_argument, 0, 'l' }, |
27 | { "output", required_argument, 0, 'o' }, | 31 | { "output", required_argument, 0, 'o' }, |
32 | { "password", required_argument, 0, 'p' }, | ||
33 | { "user", required_argument, 0, 'u' }, | ||
28 | { "version", no_argument, 0, 'v', }, | 34 | { "version", no_argument, 0, 'v', }, |
29 | { 0, 0, 0, 0 } | 35 | { 0, 0, 0, 0 } |
30 | }; | 36 | }; |
... | @@ -34,77 +40,212 @@ int db_make (char *input_name, char *output_name); | ... | @@ -34,77 +40,212 @@ int db_make (char *input_name, char *output_name); |
34 | static void help (void); | 40 | static void help (void); |
35 | static void version (void); | 41 | static void version (void); |
36 | 42 | ||
43 | #define ACT_CREATE 0 | ||
44 | #define ACT_ADD 1 | ||
45 | #define ACT_DELETE 2 | ||
46 | #define ACT_LIST 3 | ||
47 | #define ACT_CHPASS 4 | ||
48 | |||
49 | struct action_data { | ||
50 | char *input_name; | ||
51 | char *output_name; | ||
52 | char *username; | ||
53 | char *passwd; | ||
54 | }; | ||
55 | |||
56 | void check_action(int action); | ||
57 | int action_create __P((struct action_data *ap)); | ||
58 | int action_add __P((struct action_data *ap)); | ||
59 | int action_delete __P((struct action_data *ap)); | ||
60 | int action_list __P((struct action_data *ap)); | ||
61 | int action_chpass __P((struct action_data *ap)); | ||
62 | |||
63 | int (*ftab[]) __P((struct action_data *)) = { | ||
64 | action_create, | ||
65 | action_add, | ||
66 | action_delete, | ||
67 | action_list, | ||
68 | action_chpass | ||
69 | }; | ||
70 | |||
37 | int | 71 | int |
38 | main(int argc, char **argv) | 72 | main(int argc, char **argv) |
39 | { | 73 | { |
40 | int c; | 74 | int c; |
41 | int list = 0; | 75 | int action = -1; |
42 | char *input_name = NULL; | 76 | struct action_data adata; |
43 | char *output_name = NULL; | 77 | |
78 | memset (&adata, 0, sizeof adata); | ||
44 | 79 | ||
45 | while ((c = getopt_long (argc, argv, short_options, long_options, NULL)) | 80 | while ((c = getopt_long (argc, argv, short_options, long_options, NULL)) |
46 | != -1) | 81 | != -1) |
47 | { | 82 | { |
48 | switch (c) | 83 | switch (c) |
49 | { | 84 | { |
85 | case 'a': | ||
86 | check_action (action); | ||
87 | action = ACT_ADD; | ||
88 | break; | ||
89 | |||
90 | case 'c': | ||
91 | check_action (action); | ||
92 | action = ACT_CREATE; | ||
93 | break; | ||
94 | |||
95 | case 'l': | ||
96 | check_action (action); | ||
97 | action = ACT_LIST; | ||
98 | break; | ||
99 | |||
100 | case 'd': | ||
101 | check_action (action); | ||
102 | action = ACT_DELETE; | ||
103 | break; | ||
104 | |||
105 | case 'p': | ||
106 | adata.passwd = optarg; | ||
107 | break; | ||
108 | |||
109 | case 'm': | ||
110 | check_action (action); | ||
111 | action = ACT_CHPASS; | ||
112 | break; | ||
113 | |||
50 | case 'f': | 114 | case 'f': |
51 | input_name = optarg; | 115 | adata.input_name = optarg; |
52 | break; | 116 | break; |
117 | |||
53 | case 'h': | 118 | case 'h': |
54 | help (); | 119 | help (); |
55 | break; | 120 | break; |
56 | case 'l': | 121 | |
57 | list = 1; | ||
58 | break; | ||
59 | case 'o': | 122 | case 'o': |
60 | output_name = optarg; | 123 | adata.output_name = optarg; |
124 | break; | ||
125 | |||
126 | case 'u': | ||
127 | adata.username = optarg; | ||
61 | break; | 128 | break; |
129 | |||
62 | case 'v': | 130 | case 'v': |
63 | version (); | 131 | version (); |
64 | break; | 132 | break; |
133 | |||
65 | default: | 134 | default: |
66 | break; | 135 | break; |
67 | } | 136 | } |
68 | } | 137 | } |
69 | 138 | ||
70 | if (list) | 139 | if (action == -1) |
71 | return db_list (input_name, output_name); | 140 | { |
141 | /* Deduce the default action */ | ||
142 | if (getuid () == 0) | ||
143 | action = ACT_LIST; | ||
144 | else | ||
145 | action = ACT_CHPASS; | ||
146 | } | ||
72 | 147 | ||
73 | return db_make (input_name, output_name); | 148 | return (*ftab[action]) (&adata); |
74 | } | 149 | } |
75 | 150 | ||
151 | void | ||
152 | check_action (int action) | ||
153 | { | ||
154 | if (action != -1) | ||
155 | { | ||
156 | mu_error ("You may not specify more than one `-aldp' option"); | ||
157 | exit (1); | ||
158 | } | ||
159 | } | ||
160 | |||
161 | int | ||
162 | check_user_perm (int action, struct action_data *ap) | ||
163 | { | ||
164 | struct stat sb; | ||
165 | struct passwd *pw; | ||
166 | uid_t uid; | ||
167 | |||
168 | if (!ap->input_name) | ||
169 | ap->input_name = APOP_PASSFILE; | ||
170 | |||
171 | if (mu_dbm_stat (ap->input_name, &sb)) | ||
172 | { | ||
173 | mu_error ("can't stat %s: %s", ap->input_name, strerror (errno)); | ||
174 | exit (1); | ||
175 | } | ||
176 | |||
177 | uid = getuid (); | ||
178 | if (uid == 0 || sb.st_uid == uid) | ||
179 | return 0; | ||
180 | |||
181 | if (ap->username) | ||
182 | { | ||
183 | mu_error ("Only the file owner can use --username"); | ||
184 | exit (1); | ||
185 | } | ||
186 | |||
187 | if (action != ACT_CHPASS) | ||
188 | { | ||
189 | mu_error ("Operation not allowed"); | ||
190 | exit (1); | ||
191 | } | ||
192 | pw = getpwuid (uid); | ||
193 | if (!pw) | ||
194 | exit (1); | ||
195 | ap->username = pw->pw_name; | ||
196 | return 1; | ||
197 | } | ||
76 | 198 | ||
77 | int | 199 | int |
78 | db_list (char *input_name, char *output_name) | 200 | action_list (struct action_data *ap) |
79 | { | 201 | { |
80 | FILE *fp; | 202 | FILE *fp; |
81 | DBM_FILE db; | 203 | DBM_FILE db; |
82 | DBM_DATUM key; | 204 | DBM_DATUM key; |
83 | DBM_DATUM contents; | 205 | DBM_DATUM contents; |
84 | 206 | ||
85 | if (!input_name) | 207 | check_user_perm (ACT_LIST, ap); |
86 | input_name = APOP_PASSFILE; | 208 | if (mu_dbm_open (ap->input_name, &db, MU_STREAM_READ, 0600)) |
87 | if (mu_dbm_open (input_name, &db, MU_STREAM_READ, 0600)) | ||
88 | { | 209 | { |
89 | mu_error("can't open %s: %s", input_name, strerror (errno)); | 210 | mu_error("can't open %s: %s", ap->input_name, strerror (errno)); |
90 | return 1; | 211 | return 1; |
91 | } | 212 | } |
92 | 213 | ||
93 | if (output_name) | 214 | if (ap->output_name) |
94 | { | 215 | { |
95 | fp = fopen (output_name, "w"); | 216 | fp = fopen (ap->output_name, "w"); |
96 | if (!fp) | 217 | if (!fp) |
97 | { | 218 | { |
98 | mu_error("can't create %s: %s", output_name, strerror (errno)); | 219 | mu_error("can't create %s: %s", ap->output_name, strerror (errno)); |
99 | return 1; | 220 | return 1; |
100 | } | 221 | } |
101 | } | 222 | } |
102 | else | 223 | else |
103 | fp = stdout; | 224 | fp = stdout; |
104 | 225 | ||
226 | if (ap->username) | ||
227 | { | ||
228 | memset (&key, 0, sizeof key); | ||
229 | memset (&contents, 0, sizeof contents); | ||
230 | MU_DATUM_PTR (key) = ap->username; | ||
231 | MU_DATUM_SIZE (key) = strlen (ap->username); | ||
232 | if (mu_dbm_fetch (db, key, &contents)) | ||
233 | { | ||
234 | mu_error ("no such user: %s", ap->username); | ||
235 | } | ||
236 | else | ||
237 | fprintf (fp, "%.*s: %.*s\n", | ||
238 | (int) MU_DATUM_SIZE (key), | ||
239 | (char*) MU_DATUM_PTR (key), | ||
240 | (int) MU_DATUM_SIZE (contents), | ||
241 | (char*) MU_DATUM_PTR (contents)); | ||
242 | } | ||
243 | else | ||
244 | { | ||
105 | for (key = mu_dbm_firstkey (db); MU_DATUM_PTR(key); | 245 | for (key = mu_dbm_firstkey (db); MU_DATUM_PTR(key); |
106 | key = mu_dbm_nextkey (db, key)) | 246 | key = mu_dbm_nextkey (db, key)) |
107 | { | 247 | { |
248 | memset (&contents, 0, sizeof contents); | ||
108 | mu_dbm_fetch (db, key, &contents); | 249 | mu_dbm_fetch (db, key, &contents); |
109 | fprintf (fp, "%.*s: %.*s\n", | 250 | fprintf (fp, "%.*s: %.*s\n", |
110 | (int) MU_DATUM_SIZE (key), | 251 | (int) MU_DATUM_SIZE (key), |
... | @@ -112,13 +253,15 @@ db_list (char *input_name, char *output_name) | ... | @@ -112,13 +253,15 @@ db_list (char *input_name, char *output_name) |
112 | (int) MU_DATUM_SIZE (contents), | 253 | (int) MU_DATUM_SIZE (contents), |
113 | (char*) MU_DATUM_PTR (contents)); | 254 | (char*) MU_DATUM_PTR (contents)); |
114 | } | 255 | } |
256 | } | ||
257 | |||
115 | mu_dbm_close (db); | 258 | mu_dbm_close (db); |
116 | fclose (fp); | 259 | fclose (fp); |
117 | return 0; | 260 | return 0; |
118 | } | 261 | } |
119 | 262 | ||
120 | int | 263 | int |
121 | db_make (char *input_name, char *output_name) | 264 | action_create (struct action_data *ap) |
122 | { | 265 | { |
123 | FILE *fp; | 266 | FILE *fp; |
124 | DBM_FILE db; | 267 | DBM_FILE db; |
... | @@ -127,26 +270,26 @@ db_make (char *input_name, char *output_name) | ... | @@ -127,26 +270,26 @@ db_make (char *input_name, char *output_name) |
127 | char buf[256]; | 270 | char buf[256]; |
128 | int line = 0; | 271 | int line = 0; |
129 | 272 | ||
130 | if (input_name) | 273 | if (ap->input_name) |
131 | { | 274 | { |
132 | fp = fopen (input_name, "r"); | 275 | fp = fopen (ap->input_name, "r"); |
133 | if (!fp) | 276 | if (!fp) |
134 | { | 277 | { |
135 | mu_error("can't create %s: %s", input_name, strerror (errno)); | 278 | mu_error("can't create %s: %s", ap->input_name, strerror (errno)); |
136 | return 1; | 279 | return 1; |
137 | } | 280 | } |
138 | } | 281 | } |
139 | else | 282 | else |
140 | { | 283 | { |
141 | input_name = ""; | 284 | ap->input_name = ""; |
142 | fp = stdin; | 285 | fp = stdin; |
143 | } | 286 | } |
144 | 287 | ||
145 | if (!output_name) | 288 | if (!ap->output_name) |
146 | output_name = APOP_PASSFILE; | 289 | ap->output_name = APOP_PASSFILE; |
147 | if (mu_dbm_open (output_name, &db, MU_STREAM_CREAT, 0600)) | 290 | if (mu_dbm_open (ap->output_name, &db, MU_STREAM_CREAT, 0600)) |
148 | { | 291 | { |
149 | mu_error("can't create %s: %s", output_name, strerror (errno)); | 292 | mu_error("can't create %s: %s", ap->output_name, strerror (errno)); |
150 | return 1; | 293 | return 1; |
151 | } | 294 | } |
152 | 295 | ||
... | @@ -176,7 +319,7 @@ db_make (char *input_name, char *output_name) | ... | @@ -176,7 +319,7 @@ db_make (char *input_name, char *output_name) |
176 | 319 | ||
177 | if (argc != 3 || argv[1][0] != ':' || argv[1][1] != 0) | 320 | if (argc != 3 || argv[1][0] != ':' || argv[1][1] != 0) |
178 | { | 321 | { |
179 | mu_error ("%s:%d: malformed line", input_name, line); | 322 | mu_error ("%s:%d: malformed line", ap->input_name, line); |
180 | argcv_free (argc, argv); | 323 | argcv_free (argc, argv); |
181 | continue; | 324 | continue; |
182 | } | 325 | } |
... | @@ -189,7 +332,7 @@ db_make (char *input_name, char *output_name) | ... | @@ -189,7 +332,7 @@ db_make (char *input_name, char *output_name) |
189 | MU_DATUM_SIZE (contents) = strlen (argv[2]); | 332 | MU_DATUM_SIZE (contents) = strlen (argv[2]); |
190 | 333 | ||
191 | if (mu_dbm_insert (db, key, contents, 1)) | 334 | if (mu_dbm_insert (db, key, contents, 1)) |
192 | mu_error ("%s:%d: can't store datum", input_name, line); | 335 | mu_error ("%s:%d: can't store datum", ap->input_name, line); |
193 | 336 | ||
194 | argcv_free (argc, argv); | 337 | argcv_free (argc, argv); |
195 | } | 338 | } |
... | @@ -198,17 +341,181 @@ db_make (char *input_name, char *output_name) | ... | @@ -198,17 +341,181 @@ db_make (char *input_name, char *output_name) |
198 | return 0; | 341 | return 0; |
199 | } | 342 | } |
200 | 343 | ||
344 | int | ||
345 | open_io (int action, struct action_data *ap, DBM_FILE *db, int *not_owner) | ||
346 | { | ||
347 | int rc = check_user_perm (action, ap); | ||
348 | if (not_owner) | ||
349 | *not_owner = rc; | ||
350 | if (mu_dbm_open (ap->input_name, db, MU_STREAM_RDWR, 0600)) | ||
351 | { | ||
352 | mu_error("can't open %s: %s", ap->input_name, strerror (errno)); | ||
353 | return 1; | ||
354 | } | ||
355 | return 0; | ||
356 | } | ||
357 | |||
358 | void | ||
359 | fill_pass (struct action_data *ap) | ||
360 | { | ||
361 | if (!ap->passwd) | ||
362 | { | ||
363 | char *p; | ||
364 | |||
365 | while (1) { | ||
366 | if (ap->passwd) | ||
367 | free (ap->passwd); | ||
368 | p = getpass ("Password:"); | ||
369 | if (!p) | ||
370 | exit (1); | ||
371 | ap->passwd = strdup (p); | ||
372 | p = getpass ("Confirm :"); | ||
373 | if (strcmp (ap->passwd, p) == 0) | ||
374 | break; | ||
375 | mu_error ("Passwords differ. Please retry."); | ||
376 | } | ||
377 | } | ||
378 | } | ||
379 | |||
380 | int | ||
381 | action_add (struct action_data *ap) | ||
382 | { | ||
383 | DBM_FILE db; | ||
384 | DBM_DATUM key; | ||
385 | DBM_DATUM contents; | ||
386 | int rc; | ||
387 | |||
388 | if (!ap->username) | ||
389 | { | ||
390 | mu_error ("missing username to add"); | ||
391 | return 1; | ||
392 | } | ||
393 | |||
394 | if (open_io (ACT_ADD, ap, &db, NULL)) | ||
395 | return 1; | ||
396 | |||
397 | fill_pass (ap); | ||
398 | |||
399 | memset (&key, 0, sizeof key); | ||
400 | memset (&contents, 0, sizeof contents); | ||
401 | MU_DATUM_PTR (key) = ap->username; | ||
402 | MU_DATUM_SIZE (key) = strlen (ap->username); | ||
403 | MU_DATUM_PTR (contents) = ap->passwd; | ||
404 | MU_DATUM_SIZE (contents) = strlen (ap->passwd); | ||
405 | |||
406 | rc = mu_dbm_insert (db, key, contents, 1); | ||
407 | if (rc) | ||
408 | mu_error ("can't store datum"); | ||
409 | |||
410 | mu_dbm_close (db); | ||
411 | return rc; | ||
412 | } | ||
413 | |||
414 | int | ||
415 | action_delete (struct action_data *ap) | ||
416 | { | ||
417 | DBM_FILE db; | ||
418 | DBM_DATUM key; | ||
419 | int rc; | ||
420 | |||
421 | if (!ap->username) | ||
422 | { | ||
423 | mu_error ("missing username to delete"); | ||
424 | return 1; | ||
425 | } | ||
426 | |||
427 | if (open_io (ACT_DELETE, ap, &db, NULL)) | ||
428 | return 1; | ||
429 | |||
430 | MU_DATUM_PTR (key) = ap->username; | ||
431 | MU_DATUM_SIZE (key) = strlen (ap->username); | ||
432 | |||
433 | rc = mu_dbm_delete (db, key); | ||
434 | if (rc) | ||
435 | mu_error ("can't remove record for %s", ap->username); | ||
436 | |||
437 | mu_dbm_close (db); | ||
438 | return rc; | ||
439 | } | ||
440 | |||
441 | int | ||
442 | action_chpass (struct action_data *ap) | ||
443 | { | ||
444 | DBM_FILE db; | ||
445 | DBM_DATUM key; | ||
446 | DBM_DATUM contents; | ||
447 | int rc; | ||
448 | int not_owner; | ||
449 | |||
450 | if (open_io (ACT_CHPASS, ap, &db, ¬_owner)) | ||
451 | return 1; | ||
452 | |||
453 | if (!ap->username) | ||
454 | { | ||
455 | mu_error ("missing username"); | ||
456 | return 1; | ||
457 | } | ||
458 | |||
459 | memset (&key, 0, sizeof key); | ||
460 | memset (&contents, 0, sizeof contents); | ||
461 | |||
462 | MU_DATUM_PTR (key) = ap->username; | ||
463 | MU_DATUM_SIZE (key) = strlen (ap->username); | ||
464 | if (mu_dbm_fetch (db, key, &contents)) | ||
465 | { | ||
466 | mu_error ("no such user: %s", ap->username); | ||
467 | return 1; | ||
468 | } | ||
469 | |||
470 | if (not_owner) | ||
471 | { | ||
472 | char *oldpass, *p; | ||
473 | |||
474 | oldpass = xmalloc (MU_DATUM_SIZE (contents) + 1); | ||
475 | memcpy (oldpass, MU_DATUM_PTR (contents), MU_DATUM_SIZE (contents)); | ||
476 | oldpass[MU_DATUM_SIZE (contents)] = 0; | ||
477 | p = getpass ("Old Password:"); | ||
478 | if (!p) | ||
479 | return 1; | ||
480 | if (strcmp (oldpass, p)) | ||
481 | { | ||
482 | mu_error ("Sorry"); | ||
483 | return 1; | ||
484 | } | ||
485 | } | ||
486 | |||
487 | fill_pass (ap); | ||
488 | |||
489 | MU_DATUM_PTR (contents) = ap->passwd; | ||
490 | MU_DATUM_SIZE (contents) = strlen (ap->passwd); | ||
491 | rc = mu_dbm_insert (db, key, contents, 1); | ||
492 | if (rc) | ||
493 | mu_error ("can't replace datum"); | ||
494 | |||
495 | mu_dbm_close (db); | ||
496 | return rc; | ||
497 | } | ||
498 | |||
201 | static void | 499 | static void |
202 | help () | 500 | help () |
203 | { | 501 | { |
204 | printf ("Usage: popauth [OPTIONS]\n"); | 502 | printf ("Usage: popauth [OPTIONS] [ACTION]\n"); |
205 | printf ("Manipulates pop3d authentication database.\n\n"); | 503 | printf ("Manipulates pop3d authentication database.\n\n"); |
504 | printf ("Options are:\n"); | ||
206 | printf (" -f, --file=FILE Read input from FILE (default stdin)\n"); | 505 | printf (" -f, --file=FILE Read input from FILE (default stdin)\n"); |
207 | printf (" -o, --output=FILE Direct output to FILE\n"); | 506 | printf (" -o, --output=FILE Direct output to FILE\n"); |
208 | printf (" -l, --list List the contents of DBM file\n"); | ||
209 | printf (" -h, --help Display this help and exit\n"); | 507 | printf (" -h, --help Display this help and exit\n"); |
508 | printf (" -u, --user=USERNAME Specify the user name\n"); | ||
509 | printf (" -p, --password=PASS Specify user's password\n"); | ||
210 | printf (" -v, --version Display program version\n"); | 510 | printf (" -v, --version Display program version\n"); |
211 | printf ("\nDefault action is to convert plaintext to DBM file.\n"); | 511 | printf ("Actions are:\n"); |
512 | printf (" -a, --add Add user\n"); | ||
513 | printf (" -l, --list List the contents of DBM file\n"); | ||
514 | printf (" -d, --delete Delete the given user\n"); | ||
515 | printf (" -m, --modify Modify user's record (change password)\n"); | ||
516 | printf ("\nDefault action is:\n"); | ||
517 | printf (" For the file owner: --list\n"); | ||
518 | printf (" For a user: --modify --username <username>\n"); | ||
212 | printf ("\nReport bugs to bug-mailutils@gnu.org\n"); | 519 | printf ("\nReport bugs to bug-mailutils@gnu.org\n"); |
213 | exit (EXIT_SUCCESS); | 520 | exit (EXIT_SUCCESS); |
214 | } | 521 | } | ... | ... |
-
Please register or sign in to post a comment