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"
40 #include <sys/param.h>
43 /* The help file for the user mgmt screen */
44 #define USER_HELPFILE "usermgmt"
46 /* XXX should they be moved out to sysinstall.h? */
47 #define GNAME_FIELD_LEN 32
48 #define GID_FIELD_LEN 11
49 #define GMEMB_FIELD_LEN 64
51 #define UID_FIELD_LEN 11
52 #define UGROUP_FIELD_LEN GNAME_FIELD_LEN
53 #define GECOS_FIELD_LEN 64
54 #define UMEMB_FIELD_LEN GMEMB_FIELD_LEN
55 #define HOMEDIR_FIELD_LEN 48
56 #define SHELL_FIELD_LEN 48
57 #define PASSWD_FIELD_LEN 32
59 /* These are nasty, but they make the layout structure a lot easier ... */
61 static char gname[GNAME_FIELD_LEN],
63 gmemb[GMEMB_FIELD_LEN],
64 uname[UT_NAMESIZE + 1],
65 passwd[PASSWD_FIELD_LEN],
66 confpasswd[PASSWD_FIELD_LEN],
68 ugroup[UGROUP_FIELD_LEN],
69 gecos[GECOS_FIELD_LEN],
70 umemb[UMEMB_FIELD_LEN],
71 homedir[HOMEDIR_FIELD_LEN],
72 shell[SHELL_FIELD_LEN];
73 #define CLEAR(v) memset(v, 0, sizeof v)
75 static int okbutton, cancelbutton;
78 /* What the screen size is meant to be */
79 #define USER_DIALOG_Y 0
80 #define USER_DIALOG_X 8
81 #define USER_DIALOG_WIDTH COLS - 16
82 #define USER_DIALOG_HEIGHT LINES - 1
84 /* The group configuration menu. */
85 static Layout groupLayout[] = {
86 #define LAYOUT_GNAME 0
87 { 4, 10, 20, GNAME_FIELD_LEN - 1,
88 "Group name:", "The alphanumeric name of the new group (mandatory)",
89 gname, STRINGOBJ, NULL },
91 { 4, 38, 10, GID_FIELD_LEN - 1,
92 "GID:", "The numerical ID for this group (leave blank for automatic choice)",
93 gid, STRINGOBJ, NULL },
94 #define LAYOUT_GMEMB 2
95 { 11, 10, 40, GMEMB_FIELD_LEN - 1,
96 "Group members:", "Who belongs to this group (i.e., gets access rights for it)",
97 gmemb, STRINGOBJ, NULL },
98 #define LAYOUT_OKBUTTON 3
100 "OK", "Select this if you are happy with these settings",
101 &okbutton, BUTTONOBJ, NULL },
102 #define LAYOUT_CANCELBUTTON 4
104 "CANCEL", "Select this if you wish to cancel this screen",
105 &cancelbutton, BUTTONOBJ, NULL },
109 /* The user configuration menu. */
110 static Layout userLayout[] = {
111 #define LAYOUT_UNAME 0
112 { 2, 6, UT_NAMESIZE, UT_NAMESIZE + 1,
113 "Login ID:", "The login name of the new user (mandatory)",
114 uname, STRINGOBJ, NULL },
116 { 2, 23, 8, UID_FIELD_LEN - 1,
117 "UID:", "The numerical ID for this user (leave blank for automatic choice)",
118 uid, STRINGOBJ, NULL },
119 #define LAYOUT_UGROUP 2
120 { 2, 33, 8, UGROUP_FIELD_LEN - 1,
121 "Group:", "The login group name for this user (leave blank for automatic choice)",
122 ugroup, STRINGOBJ, NULL },
123 #define LAYOUT_PASSWD 3
124 { 6, 6, 20, PASSWD_FIELD_LEN - 1,
125 "Password:", "The password for this user (enter this field with care!)",
126 passwd, NO_ECHO_OBJ(STRINGOBJ), NULL },
127 #define LAYOUT_CONFPASSWD 4
128 { 6, 28, 20, PASSWD_FIELD_LEN - 1,
129 "Confirm Password:", "Confirm what you typed for the password",
130 confpasswd, NO_ECHO_OBJ(STRINGOBJ), NULL },
131 #define LAYOUT_GECOS 5
132 { 10, 6, 33, GECOS_FIELD_LEN - 1,
133 "Full name:", "The user's full name (comment)",
134 gecos, STRINGOBJ, NULL },
135 #define LAYOUT_UMEMB 6
136 { 10, 43, 15, UMEMB_FIELD_LEN - 1,
137 "Member groups:", "The groups this user belongs to (i.e. gets access rights for)",
138 umemb, STRINGOBJ, NULL },
139 #define LAYOUT_HOMEDIR 7
140 { 14, 6, 20, HOMEDIR_FIELD_LEN - 1,
141 "Home directory:", "The user's home directory (leave blank for default)",
142 homedir, STRINGOBJ, NULL },
143 #define LAYOUT_SHELL 8
144 { 14, 29, 29, SHELL_FIELD_LEN - 1,
145 "Login shell:", "The user's login shell (leave blank for default)",
146 shell, STRINGOBJ, NULL },
147 #define LAYOUT_U_OKBUTTON 9
149 "OK", "Select this if you are happy with these settings",
150 &okbutton, BUTTONOBJ, NULL },
151 #define LAYOUT_U_CANCELBUTTON 10
153 "CANCEL", "Select this if you wish to cancel this screen",
154 &cancelbutton, BUTTONOBJ, NULL },
166 /* Check for the settings on the screen. */
169 verifyGroupSettings(void)
174 if (strlen(gname) == 0) {
175 feepout("The group name field must not be empty!");
178 snprintf(tmp, 256, "pw group show -q -n %s > /dev/null", gname);
179 if (vsystem("%s", tmp) == 0) {
180 feepout("This group name is already in use.");
183 if (strlen(gid) > 0) {
184 lgid = strtoul(gid, &cp, 10);
185 if (lgid == 0 || lgid > GID_MAX || (*cp != '\0' && !isspace(*cp))) {
186 feepout("The GID must be a number between 1 and 4294967295.");
190 if (strlen(gmemb) > 0) {
191 if (strpbrk(gmemb, " \t") != NULL) {
192 feepout("The group member list must not contain any whitespace;\n"
193 "use commas to separate the names.");
196 #ifndef notyet /* XXX */
197 feepout("Sorry, the group member list feature\n"
198 "is currently not yet implemented.");
207 * Ask pw(8) to fill in the blanks for us.
208 * Works solely on the global variables.
221 "pw", "group", "next", 0
225 if ((pid = fork()) == 0)
230 for (i = getdtablesize(); i > 2; i--)
233 execv("/usr/sbin/pw", vec);
234 msgDebug("Cannot execv() /usr/sbin/pw.\n");
243 while((l = read(pfd[0], &tmp[i], amnt)) > 0)
256 if (WIFSIGNALED(i) || WEXITSTATUS(i) != 0)
259 if ((cp = strchr(tmp, '\n')) != NULL)
261 strncpy(gid, tmp, sizeof gid);
266 addGroup(WINDOW *ds_win)
275 "pw", "group", "add", "-n", 0, "-g", 0, 0
280 msgNotify("Adding group \"%s\"...", gname);
283 if ((pid = fork()) == 0)
288 for (i = getdtablesize(); i > 2; i--)
291 vec[VEC_GNAME] = gname;
296 vec[VEC_GID - 1] = 0;
298 execv("/usr/sbin/pw", vec);
299 msgDebug("Cannot execv() /usr/sbin/pw.\n");
308 while((l = read(pfd[0], &tmp[i], amnt)) > 0)
322 msgDebug("pw(8) exited with signal %d.\n", WTERMSIG(i));
323 else if(WEXITSTATUS(i))
326 if(strncmp(tmp, "pw: ", 4) == 0)
328 tmp[sizeof tmp - 1] = '\0'; /* sanity */
329 msgConfirm("The `pw' command exited with an error status.\n"
330 "Its error message was:\n\n%s",
339 userAddGroup(dialogMenuItem *self)
341 WINDOW *ds_win, *save;
342 ComposeObj *obj = NULL;
343 int n = 0, cancel = FALSE, ret;
344 int max, firsttime = TRUE;
346 if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
347 msgConfirm("This option may only be used after the system is installed, sorry!");
348 return DITEM_FAILURE;
352 dialog_clear_norefresh();
353 /* We need a curses window */
354 if (!(ds_win = openLayoutDialog(USER_HELPFILE, " User and Group Management ",
355 USER_DIALOG_X, USER_DIALOG_Y, USER_DIALOG_WIDTH, USER_DIALOG_HEIGHT))) {
357 msgConfirm("Cannot open addgroup dialog window!!");
358 return(DITEM_FAILURE);
361 /* Draw a group entry box */
362 draw_box(ds_win, USER_DIALOG_Y + 2, USER_DIALOG_X + 8, USER_DIALOG_HEIGHT - 8,
363 USER_DIALOG_WIDTH - 17, dialog_attr, border_attr);
364 wattrset(ds_win, dialog_attr);
365 mvwaddstr(ds_win, USER_DIALOG_Y + 2, USER_DIALOG_X + 22, " Add a new group ");
371 /* Some more initialisation before we go into the main input loop */
372 obj = initLayoutDialog(ds_win, groupLayout, USER_DIALOG_X, USER_DIALOG_Y, &max);
375 cancelbutton = okbutton = 0;
377 /* fill in the blanks, well, just the GID */
379 RefreshStringObj(groupLayout[LAYOUT_GID].obj);
383 while (layoutDialogLoop(ds_win, groupLayout, &obj, &n, max, &cancelbutton, &cancel));
385 if (!cancel && !verifyGroupSettings())
388 /* Clear this crap off the screen */
390 dialog_clear_norefresh();
403 /* Check for the settings on the screen. */
406 verifyUserSettings(WINDOW *ds_win)
413 if (strlen(uname) == 0) {
414 feepout("The user name field must not be empty!");
417 snprintf(tmp, 256, "pw user show -q -n %s > /dev/null", uname);
418 if (vsystem("%s", tmp) == 0) {
419 feepout("This user name is already in use.");
422 if (strlen(uid) > 0) {
423 luid = strtoul(uid, &cp, 10);
424 if (luid == 0 || luid > UID_MAX || (*cp != '\0' && !isspace(*cp))) {
425 feepout("The UID must be a number between 1 and 4294967295.");
429 if (strcmp(passwd, confpasswd)) {
430 feepout("Passwords don't match");
433 if ((homedir[0]!=0) && (homedir[0]!='/')) {
434 feepout("The pathname for home directories must begin with a '/'.");
437 if (strlen(shell) > 0) {
439 while((cp = getusershell()) != NULL)
440 if (strcmp(cp, shell) == 0)
445 rv = msgYesNo("Warning:\n\n"
446 "The requested shell \"%s\" is not\n"
447 "a valid user shell.\n\n"
448 "Use it anyway?\n", shell);
451 if (rv != DITEM_SUCCESS)
457 if (strlen(umemb) > 0) {
458 if (strpbrk(umemb, " \t") != NULL) {
459 feepout("The member groups list must not contain any whitespace;\n"
460 "use commas to separate the names.");
469 * Ask pw(8) to fill in the blanks for us.
470 * Works solely on the global variables.
477 char tmp[256], *cp, *cp2;
483 "pw", "user", "add", "-N", "-n", 0, 0
488 if ((pid = fork()) == 0)
493 for (i = getdtablesize(); i > 2; i--)
496 vec[VEC_UNAME] = uname;
498 execv("/usr/sbin/pw", vec);
499 msgDebug("Cannot execv() /usr/sbin/pw.\n");
508 while((l = read(pfd[0], &tmp[i], amnt)) > 0)
521 if (WIFSIGNALED(i) || WEXITSTATUS(i) != 0)
524 if ((cp = strchr(tmp, '\n')) != NULL)
526 if ((cp = strchr(tmp, ':')) == NULL || (cp = strchr(++cp, ':')) == NULL)
529 if ((cp2 = strchr(cp, ':')) == NULL)
532 strncpy(uid, cp, sizeof uid);
534 if ((cp2 = strchr(cp, ':')) == NULL)
537 #ifdef notyet /* XXX pw user add -g doesn't accept a numerical GID */
538 strncpy(ugroup, cp, sizeof ugroup);
541 if ((cp2 = strchr(cp, ':')) == NULL || (cp2 = strchr(++cp2, ':')) == NULL ||
542 (cp = cp2 = strchr(++cp2, ':')) == NULL || (cp2 = strchr(++cp2, ':')) == NULL)
546 strncpy(gecos, cp, sizeof gecos);
548 if ((cp2 = strchr(cp, ':')) == NULL)
552 strncpy(shell, cp2, sizeof shell);
558 addUser(WINDOW *ds_win)
561 int pfd[2], ipfd[2], i, j;
567 * pw user add -m -n uname -g grp -u uid -c comment -d homedir -s shell -G grplist -h 0
571 "pw", "user", "add", "-m", "-n", /* ... */
575 msgNotify("Adding user \"%s\"...", uname);
579 if ((pid = fork()) == 0)
585 for (i = getdtablesize(); i > 2; i--)
588 vec[i = VEC_UNAME] = uname;
590 #define ADDVEC(var, option) do { if (strlen(var) > 0) { vec[i++] = option; vec[i++] = var; } } while (0)
591 ADDVEC(ugroup, "-g");
594 ADDVEC(homedir, "-d");
603 execv("/usr/sbin/pw", vec);
604 msgDebug("Cannot execv() /usr/sbin/pw.\n");
614 write(ipfd[1], passwd, strlen(passwd));
618 while((l = read(pfd[0], &tmp[i], amnt)) > 0)
634 msg = "The `pw' command exited with signal %d.\n";
637 else if((j = WEXITSTATUS(i)))
640 if(strncmp(tmp, "pw: ", 4) == 0)
642 tmp[sizeof tmp - 1] = '\0'; /* sanity */
643 if (j == EX_DATAERR || j == EX_NOUSER || j == EX_SOFTWARE)
644 msgConfirm("The `pw' command exited with an error status.\n"
645 "Its error message was:\n\n%s",
649 msg = "The `pw' command exited with unexpected status %d.\n";
652 msgDebug("Command stdout and stderr was:\n\n%s", tmp);
657 msgConfirm("You will need to enter a password for this user\n"
658 "later, using the passwd(1) command from the shell.\n\n"
659 "The account for `%s' is currently still disabled.",
667 userAddUser(dialogMenuItem *self)
669 WINDOW *ds_win, *save;
670 ComposeObj *obj = NULL;
671 int n = 0, cancel = FALSE, ret;
672 int max, firsttime = TRUE, filled=0;
674 if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
675 msgConfirm("This option may only be used after the system is installed, sorry!");
676 return DITEM_FAILURE;
680 dialog_clear_norefresh();
682 /* We need a curses window */
683 if (!(ds_win = openLayoutDialog(USER_HELPFILE, " User and Group Management ",
684 USER_DIALOG_X, USER_DIALOG_Y, USER_DIALOG_WIDTH, USER_DIALOG_HEIGHT))) {
686 msgConfirm("Cannot open adduser dialog window!!");
687 return(DITEM_FAILURE);
690 /* Draw a user entry box */
691 draw_box(ds_win, USER_DIALOG_Y + 1, USER_DIALOG_X + 3, USER_DIALOG_HEIGHT - 6,
692 USER_DIALOG_WIDTH - 6, dialog_attr, border_attr);
693 wattrset(ds_win, dialog_attr);
694 mvwaddstr(ds_win, USER_DIALOG_Y + 1, USER_DIALOG_X + 24, " Add a new user ");
706 /* Some more initialisation before we go into the main input loop */
707 obj = initLayoutDialog(ds_win, userLayout, USER_DIALOG_X, USER_DIALOG_Y, &max);
710 cancelbutton = okbutton = 0;
712 /* fill in the blanks, well, just the GID */
714 RefreshStringObj(userLayout[LAYOUT_UID].obj);
715 RefreshStringObj(userLayout[LAYOUT_UGROUP].obj);
716 RefreshStringObj(userLayout[LAYOUT_GECOS].obj);
717 RefreshStringObj(userLayout[LAYOUT_UMEMB].obj);
718 RefreshStringObj(userLayout[LAYOUT_HOMEDIR].obj);
719 RefreshStringObj(userLayout[LAYOUT_SHELL].obj);
723 while (layoutDialogLoop(ds_win, userLayout, &obj, &n, max, &cancelbutton, &cancel)) {
724 /* Prevent this from being irritating if user really means NO */
726 if ((uname[0]) && !homedir[0]) {
727 SAFE_STRCPY(homedir,"/home/");
728 strcat(homedir,uname);
729 RefreshStringObj(userLayout[LAYOUT_HOMEDIR].obj);
735 if (!cancel && !verifyUserSettings(ds_win))
738 /* Clear this crap off the screen */
740 dialog_clear_norefresh();