5 * Jörg Wunsch. All rights reserved.
7 * The basic structure has been taken from tcpip.c, which is:
10 * Gary J Palmer. All rights reserved.
11 * Jordan K Hubbard. All rights reserved.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer,
18 * verbatim and that no modifications are made prior to this
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution.
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
30 * OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
32 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 #include "sysinstall.h"
39 #include <sys/param.h>
42 /* The help file for the user mgmt screen */
43 #define USER_HELPFILE "usermgmt"
45 /* XXX should they be moved out to sysinstall.h? */
46 #define GNAME_FIELD_LEN 32
47 #define GID_FIELD_LEN 11
48 #define GMEMB_FIELD_LEN 64
49 #define UNAME_FIELD_LEN MAXLOGNAME
50 #define UID_FIELD_LEN 11
51 #define UGROUP_FIELD_LEN GNAME_FIELD_LEN
52 #define GECOS_FIELD_LEN 64
53 #define UMEMB_FIELD_LEN GMEMB_FIELD_LEN
54 #define HOMEDIR_FIELD_LEN 48
55 #define SHELL_FIELD_LEN 48
56 #define PASSWD_FIELD_LEN 32
58 /* These are nasty, but they make the layout structure a lot easier ... */
60 static char gname[GNAME_FIELD_LEN],
62 gmemb[GMEMB_FIELD_LEN],
63 uname[UNAME_FIELD_LEN],
64 passwd[PASSWD_FIELD_LEN],
65 confpasswd[PASSWD_FIELD_LEN],
67 ugroup[UGROUP_FIELD_LEN],
68 gecos[GECOS_FIELD_LEN],
69 umemb[UMEMB_FIELD_LEN],
70 homedir[HOMEDIR_FIELD_LEN],
71 shell[SHELL_FIELD_LEN];
72 #define CLEAR(v) memset(v, 0, sizeof v)
74 static int okbutton, cancelbutton;
77 /* What the screen size is meant to be */
78 #define USER_DIALOG_Y 0
79 #define USER_DIALOG_X 8
80 #define USER_DIALOG_WIDTH COLS - 16
81 #define USER_DIALOG_HEIGHT LINES - 1
83 /* The group configuration menu. */
84 static Layout groupLayout[] = {
85 #define LAYOUT_GNAME 0
86 { 4, 10, 20, GNAME_FIELD_LEN - 1,
87 "Group name:", "The alphanumeric name of the new group (mandatory)",
88 gname, STRINGOBJ, NULL },
90 { 4, 38, 10, GID_FIELD_LEN - 1,
91 "GID:", "The numerical ID for this group (leave blank for automatic choice)",
92 gid, STRINGOBJ, NULL },
93 #define LAYOUT_GMEMB 2
94 { 11, 10, 40, GMEMB_FIELD_LEN - 1,
95 "Group members:", "Who belongs to this group (i.e., gets access rights for it)",
96 gmemb, STRINGOBJ, NULL },
97 #define LAYOUT_OKBUTTON 3
99 "OK", "Select this if you are happy with these settings",
100 &okbutton, BUTTONOBJ, NULL },
101 #define LAYOUT_CANCELBUTTON 4
103 "CANCEL", "Select this if you wish to cancel this screen",
104 &cancelbutton, BUTTONOBJ, NULL },
108 /* The user configuration menu. */
109 static Layout userLayout[] = {
110 #define LAYOUT_UNAME 0
111 { 2, 6, 16, UNAME_FIELD_LEN - 1,
112 "Login ID:", "The login name of the new user (mandatory)",
113 uname, STRINGOBJ, NULL },
115 { 2, 23, 8, UID_FIELD_LEN - 1,
116 "UID:", "The numerical ID for this user (leave blank for automatic choice)",
117 uid, STRINGOBJ, NULL },
118 #define LAYOUT_UGROUP 2
119 { 2, 33, 8, UGROUP_FIELD_LEN - 1,
120 "Group:", "The login group name for this user (leave blank for automatic choice)",
121 ugroup, STRINGOBJ, NULL },
122 #define LAYOUT_PASSWD 3
123 { 6, 6, 20, PASSWD_FIELD_LEN - 1,
124 "Password:", "The password for this user (enter this field with care!)",
125 passwd, NO_ECHO_OBJ(STRINGOBJ), NULL },
126 #define LAYOUT_CONFPASSWD 4
127 { 6, 28, 20, PASSWD_FIELD_LEN - 1,
128 "Confirm Password:", "Confirm what you typed for the password",
129 confpasswd, NO_ECHO_OBJ(STRINGOBJ), NULL },
130 #define LAYOUT_GECOS 5
131 { 10, 6, 33, GECOS_FIELD_LEN - 1,
132 "Full name:", "The user's full name (comment)",
133 gecos, STRINGOBJ, NULL },
134 #define LAYOUT_UMEMB 6
135 { 10, 43, 15, UMEMB_FIELD_LEN - 1,
136 "Member groups:", "The groups this user belongs to (i.e. gets access rights for)",
137 umemb, STRINGOBJ, NULL },
138 #define LAYOUT_HOMEDIR 7
139 { 14, 6, 20, HOMEDIR_FIELD_LEN - 1,
140 "Home directory:", "The user's home directory (leave blank for default)",
141 homedir, STRINGOBJ, NULL },
142 #define LAYOUT_SHELL 8
143 { 14, 29, 29, SHELL_FIELD_LEN - 1,
144 "Login shell:", "The user's login shell (leave blank for default)",
145 shell, STRINGOBJ, NULL },
146 #define LAYOUT_U_OKBUTTON 9
148 "OK", "Select this if you are happy with these settings",
149 &okbutton, BUTTONOBJ, NULL },
150 #define LAYOUT_U_CANCELBUTTON 10
152 "CANCEL", "Select this if you wish to cancel this screen",
153 &cancelbutton, BUTTONOBJ, NULL },
165 /* Check for the settings on the screen. */
168 verifyGroupSettings(void)
173 if (strlen(gname) == 0) {
174 feepout("The group name field must not be empty!");
177 snprintf(tmp, 256, "pw group show -q -n %s > /dev/null", gname);
178 if (vsystem("%s", tmp) == 0) {
179 feepout("This group name is already in use.");
182 if (strlen(gid) > 0) {
183 lgid = strtoul(gid, &cp, 10);
184 if (lgid == 0 || lgid > GID_MAX || (*cp != '\0' && !isspace(*cp))) {
185 feepout("The GID must be a number between 1 and 4294967295.");
189 if (strlen(gmemb) > 0) {
190 if (strpbrk(gmemb, " \t") != NULL) {
191 feepout("The group member list must not contain any whitespace;\n"
192 "use commas to separate the names.");
195 #ifndef notyet /* XXX */
196 feepout("Sorry, the group member list feature\n"
197 "is currently not yet implemented.");
206 * Ask pw(8) to fill in the blanks for us.
207 * Works solely on the global variables.
220 "pw", "group", "next", 0
224 if ((pid = fork()) == 0)
229 for (i = getdtablesize(); i > 2; i--)
232 execv("/usr/sbin/pw", vec);
233 msgDebug("Cannot execv() /usr/sbin/pw.\n");
242 while((l = read(pfd[0], &tmp[i], amnt)) > 0)
255 if (WIFSIGNALED(i) || WEXITSTATUS(i) != 0)
258 if ((cp = strchr(tmp, '\n')) != NULL)
260 strncpy(gid, tmp, sizeof gid);
265 addGroup(WINDOW *ds_win)
274 "pw", "group", "add", "-n", 0, "-g", 0, 0
279 msgNotify("Adding group \"%s\"...", gname);
282 if ((pid = fork()) == 0)
287 for (i = getdtablesize(); i > 2; i--)
290 vec[VEC_GNAME] = gname;
295 vec[VEC_GID - 1] = 0;
297 execv("/usr/sbin/pw", vec);
298 msgDebug("Cannot execv() /usr/sbin/pw.\n");
307 while((l = read(pfd[0], &tmp[i], amnt)) > 0)
321 msgDebug("pw(8) exited with signal %d.\n", WTERMSIG(i));
322 else if(WEXITSTATUS(i))
325 if(strncmp(tmp, "pw: ", 4) == 0)
327 tmp[sizeof tmp - 1] = '\0'; /* sanity */
328 msgConfirm("The `pw' command exited with an error status.\n"
329 "Its error message was:\n\n%s",
338 userAddGroup(dialogMenuItem *self)
340 WINDOW *ds_win, *save;
341 ComposeObj *obj = NULL;
342 int n = 0, cancel = FALSE, ret;
343 int max, firsttime = TRUE;
345 if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
346 msgConfirm("This option may only be used after the system is installed, sorry!");
347 return DITEM_FAILURE;
351 dialog_clear_norefresh();
352 /* We need a curses window */
353 if (!(ds_win = openLayoutDialog(USER_HELPFILE, " User and Group Management ",
354 USER_DIALOG_X, USER_DIALOG_Y, USER_DIALOG_WIDTH, USER_DIALOG_HEIGHT))) {
356 msgConfirm("Cannot open addgroup dialog window!!");
357 return(DITEM_FAILURE);
360 /* Draw a group entry box */
361 draw_box(ds_win, USER_DIALOG_Y + 2, USER_DIALOG_X + 8, USER_DIALOG_HEIGHT - 8,
362 USER_DIALOG_WIDTH - 17, dialog_attr, border_attr);
363 wattrset(ds_win, dialog_attr);
364 mvwaddstr(ds_win, USER_DIALOG_Y + 2, USER_DIALOG_X + 22, " Add a new group ");
370 /* Some more initialisation before we go into the main input loop */
371 obj = initLayoutDialog(ds_win, groupLayout, USER_DIALOG_X, USER_DIALOG_Y, &max);
374 cancelbutton = okbutton = 0;
376 /* fill in the blanks, well, just the GID */
378 RefreshStringObj(groupLayout[LAYOUT_GID].obj);
382 while (layoutDialogLoop(ds_win, groupLayout, &obj, &n, max, &cancelbutton, &cancel));
384 if (!cancel && !verifyGroupSettings())
387 /* Clear this crap off the screen */
389 dialog_clear_norefresh();
402 /* Check for the settings on the screen. */
405 verifyUserSettings(WINDOW *ds_win)
412 if (strlen(uname) == 0) {
413 feepout("The user name field must not be empty!");
416 snprintf(tmp, 256, "pw user show -q -n %s > /dev/null", uname);
417 if (vsystem("%s", tmp) == 0) {
418 feepout("This user name is already in use.");
421 if (strlen(uid) > 0) {
422 luid = strtoul(uid, &cp, 10);
423 if (luid == 0 || luid > UID_MAX || (*cp != '\0' && !isspace(*cp))) {
424 feepout("The UID must be a number between 1 and 4294967295.");
428 if (strcmp(passwd, confpasswd)) {
429 feepout("Passwords don't match");
432 if ((homedir[0]!=0) && (homedir[0]!='/')) {
433 feepout("The pathname for home directories must begin with a '/'.");
436 if (strlen(shell) > 0) {
438 while((cp = getusershell()) != NULL)
439 if (strcmp(cp, shell) == 0)
444 rv = msgYesNo("Warning:\n\n"
445 "The requested shell \"%s\" is not\n"
446 "a valid user shell.\n\n"
447 "Use it anyway?\n", shell);
450 if (rv != DITEM_SUCCESS)
456 if (strlen(umemb) > 0) {
457 if (strpbrk(umemb, " \t") != NULL) {
458 feepout("The member groups list must not contain any whitespace;\n"
459 "use commas to separate the names.");
468 * Ask pw(8) to fill in the blanks for us.
469 * Works solely on the global variables.
476 char tmp[256], *cp, *cp2;
482 "pw", "user", "add", "-N", "-n", 0, 0
487 if ((pid = fork()) == 0)
492 for (i = getdtablesize(); i > 2; i--)
495 vec[VEC_UNAME] = uname;
497 execv("/usr/sbin/pw", vec);
498 msgDebug("Cannot execv() /usr/sbin/pw.\n");
507 while((l = read(pfd[0], &tmp[i], amnt)) > 0)
520 if (WIFSIGNALED(i) || WEXITSTATUS(i) != 0)
523 if ((cp = strchr(tmp, '\n')) != NULL)
525 if ((cp = strchr(tmp, ':')) == NULL || (cp = strchr(++cp, ':')) == NULL)
528 if ((cp2 = strchr(cp, ':')) == NULL)
531 strncpy(uid, cp, sizeof uid);
533 if ((cp2 = strchr(cp, ':')) == NULL)
536 #ifdef notyet /* XXX pw user add -g doesn't accept a numerical GID */
537 strncpy(ugroup, cp, sizeof ugroup);
540 if ((cp2 = strchr(cp, ':')) == NULL || (cp2 = strchr(++cp2, ':')) == NULL ||
541 (cp = cp2 = strchr(++cp2, ':')) == NULL || (cp2 = strchr(++cp2, ':')) == NULL)
545 strncpy(gecos, cp, sizeof gecos);
547 if ((cp2 = strchr(cp, ':')) == NULL)
551 strncpy(shell, cp2, sizeof shell);
557 addUser(WINDOW *ds_win)
560 int pfd[2], ipfd[2], i, j;
566 * pw user add -m -n uname -g grp -u uid -c comment -d homedir -s shell -G grplist -h 0
570 "pw", "user", "add", "-m", "-n", /* ... */
574 msgNotify("Adding user \"%s\"...", uname);
578 if ((pid = fork()) == 0)
584 for (i = getdtablesize(); i > 2; i--)
587 vec[i = VEC_UNAME] = uname;
589 #define ADDVEC(var, option) do { if (strlen(var) > 0) { vec[i++] = option; vec[i++] = var; } } while (0)
590 ADDVEC(ugroup, "-g");
593 ADDVEC(homedir, "-d");
602 execv("/usr/sbin/pw", vec);
603 msgDebug("Cannot execv() /usr/sbin/pw.\n");
613 write(ipfd[1], passwd, strlen(passwd));
617 while((l = read(pfd[0], &tmp[i], amnt)) > 0)
633 msg = "The `pw' command exited with signal %d.\n";
636 else if((j = WEXITSTATUS(i)))
639 if(strncmp(tmp, "pw: ", 4) == 0)
641 tmp[sizeof tmp - 1] = '\0'; /* sanity */
642 if (j == EX_DATAERR || j == EX_NOUSER || j == EX_SOFTWARE)
643 msgConfirm("The `pw' command exited with an error status.\n"
644 "Its error message was:\n\n%s",
648 msg = "The `pw' command exited with unexpected status %d.\n";
651 msgDebug("Command stdout and stderr was:\n\n%s", tmp);
656 msgConfirm("You will need to enter a password for this user\n"
657 "later, using the passwd(1) command from the shell.\n\n"
658 "The account for `%s' is currently still disabled.",
666 userAddUser(dialogMenuItem *self)
668 WINDOW *ds_win, *save;
669 ComposeObj *obj = NULL;
670 int n = 0, cancel = FALSE, ret;
671 int max, firsttime = TRUE, filled=0;
673 if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
674 msgConfirm("This option may only be used after the system is installed, sorry!");
675 return DITEM_FAILURE;
679 dialog_clear_norefresh();
681 /* We need a curses window */
682 if (!(ds_win = openLayoutDialog(USER_HELPFILE, " User and Group Management ",
683 USER_DIALOG_X, USER_DIALOG_Y, USER_DIALOG_WIDTH, USER_DIALOG_HEIGHT))) {
685 msgConfirm("Cannot open adduser dialog window!!");
686 return(DITEM_FAILURE);
689 /* Draw a user entry box */
690 draw_box(ds_win, USER_DIALOG_Y + 1, USER_DIALOG_X + 3, USER_DIALOG_HEIGHT - 6,
691 USER_DIALOG_WIDTH - 6, dialog_attr, border_attr);
692 wattrset(ds_win, dialog_attr);
693 mvwaddstr(ds_win, USER_DIALOG_Y + 1, USER_DIALOG_X + 24, " Add a new user ");
705 /* Some more initialisation before we go into the main input loop */
706 obj = initLayoutDialog(ds_win, userLayout, USER_DIALOG_X, USER_DIALOG_Y, &max);
709 cancelbutton = okbutton = 0;
711 /* fill in the blanks, well, just the GID */
713 RefreshStringObj(userLayout[LAYOUT_UID].obj);
714 RefreshStringObj(userLayout[LAYOUT_UGROUP].obj);
715 RefreshStringObj(userLayout[LAYOUT_GECOS].obj);
716 RefreshStringObj(userLayout[LAYOUT_UMEMB].obj);
717 RefreshStringObj(userLayout[LAYOUT_HOMEDIR].obj);
718 RefreshStringObj(userLayout[LAYOUT_SHELL].obj);
722 while (layoutDialogLoop(ds_win, userLayout, &obj, &n, max, &cancelbutton, &cancel)) {
723 /* Prevent this from being irritating if user really means NO */
725 if ((uname[0]) && !homedir[0]) {
726 SAFE_STRCPY(homedir,"/home/");
727 strcat(homedir,uname);
728 RefreshStringObj(userLayout[LAYOUT_HOMEDIR].obj);
734 if (!cancel && !verifyUserSettings(ds_win))
737 /* Clear this crap off the screen */
739 dialog_clear_norefresh();