Commit c96a5f95 c96a5f955758253e6852399e0dea88d44ea32664 by Sergey Poznyakoff

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.
1 parent 224afad6
...@@ -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, &not_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 }
......