120231Sjkh/*
250479Speter * $FreeBSD$
320231Sjkh *
420231Sjkh * Copyright (c) 1996
520231Sjkh *      J�rg Wunsch. All rights reserved.
620231Sjkh *
720231Sjkh * The basic structure has been taken from tcpip.c, which is:
820231Sjkh *
920231Sjkh * Copyright (c) 1995
1020231Sjkh *      Gary J Palmer. All rights reserved.
1121243Sjkh *      Jordan K Hubbard. All rights reserved.
1220231Sjkh *
1320231Sjkh * Redistribution and use in source and binary forms, with or without
1420231Sjkh * modification, are permitted provided that the following conditions
1520231Sjkh * are met:
1620231Sjkh * 1. Redistributions of source code must retain the above copyright
1720231Sjkh *    notice, this list of conditions and the following disclaimer,
1820231Sjkh *    verbatim and that no modifications are made prior to this
1920231Sjkh *    point in the file.
2020231Sjkh * 2. Redistributions in binary form must reproduce the above copyright
2120231Sjkh *    notice, this list of conditions and the following disclaimer in the
2220231Sjkh *    documentation and/or other materials provided with the distribution.
2320231Sjkh *
2420231Sjkh * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
2520231Sjkh * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2620231Sjkh * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2720231Sjkh * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2820231Sjkh * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2920231Sjkh * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
3020231Sjkh * OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
3120231Sjkh * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
3220231Sjkh * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3320231Sjkh * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3420231Sjkh *
3520231Sjkh */
3620231Sjkh
3721243Sjkh#include "sysinstall.h"
3821243Sjkh#include <ctype.h>
3920231Sjkh#include <sys/param.h>
4020271Sjoerg#include <sysexits.h>
4120231Sjkh
4220231Sjkh/* The help file for the user mgmt screen */
4320231Sjkh#define USER_HELPFILE		"usermgmt"
4420231Sjkh
4520231Sjkh/* XXX should they be moved out to sysinstall.h? */
4620231Sjkh#define GNAME_FIELD_LEN 32
47195828Skensmith#define GID_FIELD_LEN 11
4820231Sjkh#define GMEMB_FIELD_LEN 64
49200302Sed#define UNAME_FIELD_LEN MAXLOGNAME
50195828Skensmith#define UID_FIELD_LEN 11
5120231Sjkh#define UGROUP_FIELD_LEN GNAME_FIELD_LEN
5220231Sjkh#define GECOS_FIELD_LEN 64
5320231Sjkh#define UMEMB_FIELD_LEN GMEMB_FIELD_LEN
5420231Sjkh#define HOMEDIR_FIELD_LEN 48
5520231Sjkh#define SHELL_FIELD_LEN 48
5620484Sjkh#define PASSWD_FIELD_LEN 32
5720231Sjkh
5820231Sjkh/* These are nasty, but they make the layout structure a lot easier ... */
5920231Sjkh
6020231Sjkhstatic char gname[GNAME_FIELD_LEN],
6120231Sjkh	gid[GID_FIELD_LEN],
6220231Sjkh	gmemb[GMEMB_FIELD_LEN],
63200302Sed	uname[UNAME_FIELD_LEN],
6420484Sjkh        passwd[PASSWD_FIELD_LEN],
65186183Skensmith        confpasswd[PASSWD_FIELD_LEN],
6620231Sjkh	uid[UID_FIELD_LEN],
6720231Sjkh	ugroup[UGROUP_FIELD_LEN],
6820231Sjkh	gecos[GECOS_FIELD_LEN],
6920231Sjkh	umemb[UMEMB_FIELD_LEN],
7020231Sjkh	homedir[HOMEDIR_FIELD_LEN],
7120231Sjkh	shell[SHELL_FIELD_LEN];
7220231Sjkh#define CLEAR(v)	memset(v, 0, sizeof v)
7320231Sjkh
7420231Sjkhstatic int	okbutton, cancelbutton;
7520231Sjkh
7620231Sjkh
7720231Sjkh/* What the screen size is meant to be */
7820231Sjkh#define USER_DIALOG_Y		0
7920231Sjkh#define USER_DIALOG_X		8
8020231Sjkh#define USER_DIALOG_WIDTH	COLS - 16
81186183Skensmith#define USER_DIALOG_HEIGHT	LINES - 1
8220231Sjkh
8320231Sjkh/* The group configuration menu. */
8420231Sjkhstatic Layout groupLayout[] = {
8520484Sjkh#define LAYOUT_GNAME		0
8621243Sjkh    { 4, 10, 20, GNAME_FIELD_LEN - 1,
8721243Sjkh      "Group name:", "The alphanumeric name of the new group (mandatory)",
8821243Sjkh      gname, STRINGOBJ, NULL },
8920484Sjkh#define LAYOUT_GID		1
9021243Sjkh    { 4, 38, 10, GID_FIELD_LEN - 1,
9121243Sjkh      "GID:", "The numerical ID for this group (leave blank for automatic choice)",
9221243Sjkh      gid, STRINGOBJ, NULL },
9320484Sjkh#define LAYOUT_GMEMB		2
9421243Sjkh    { 11, 10, 40, GMEMB_FIELD_LEN - 1,
9521243Sjkh      "Group members:", "Who belongs to this group (i.e., gets access rights for it)",
9621243Sjkh      gmemb, STRINGOBJ, NULL },
9720484Sjkh#define LAYOUT_OKBUTTON		3
9821243Sjkh    { 18, 15, 0, 0,
9921243Sjkh      "OK", "Select this if you are happy with these settings",
10021243Sjkh      &okbutton, BUTTONOBJ, NULL },
10120484Sjkh#define LAYOUT_CANCELBUTTON	4
10221243Sjkh    { 18, 35, 0, 0,
10321243Sjkh      "CANCEL", "Select this if you wish to cancel this screen",
10421243Sjkh      &cancelbutton, BUTTONOBJ, NULL },
105156123Sjhb    LAYOUT_END,
10620231Sjkh};
10720231Sjkh
10820231Sjkh/* The user configuration menu. */
10920231Sjkhstatic Layout userLayout[] = {
11020484Sjkh#define LAYOUT_UNAME		0
111200302Sed    { 2, 6, 16, UNAME_FIELD_LEN - 1,
11221243Sjkh      "Login ID:", "The login name of the new user (mandatory)",
11321243Sjkh      uname, STRINGOBJ, NULL },
11420484Sjkh#define LAYOUT_UID		1
115186183Skensmith    { 2, 23, 8, UID_FIELD_LEN - 1,
11621243Sjkh      "UID:", "The numerical ID for this user (leave blank for automatic choice)",
11721243Sjkh      uid, STRINGOBJ, NULL },
11820484Sjkh#define LAYOUT_UGROUP		2
119186183Skensmith    { 2, 33, 8, UGROUP_FIELD_LEN - 1,
12021243Sjkh      "Group:", "The login group name for this user (leave blank for automatic choice)",
12121243Sjkh      ugroup, STRINGOBJ, NULL },
12220484Sjkh#define LAYOUT_PASSWD		3
123186183Skensmith    { 6, 6, 20, PASSWD_FIELD_LEN - 1,
12421243Sjkh      "Password:", "The password for this user (enter this field with care!)",
12522099Sjkh      passwd, NO_ECHO_OBJ(STRINGOBJ), NULL },
126186183Skensmith#define LAYOUT_CONFPASSWD	4
127186183Skensmith    { 6, 28, 20, PASSWD_FIELD_LEN - 1,
128186183Skensmith      "Confirm Password:", "Confirm what you typed for the password",
129186183Skensmith      confpasswd, NO_ECHO_OBJ(STRINGOBJ), NULL },
130186183Skensmith#define LAYOUT_GECOS		5
131186183Skensmith    { 10, 6, 33, GECOS_FIELD_LEN - 1,
13221243Sjkh      "Full name:", "The user's full name (comment)",
13321243Sjkh      gecos, STRINGOBJ, NULL },
134186183Skensmith#define LAYOUT_UMEMB		6
135186183Skensmith    { 10, 43, 15, UMEMB_FIELD_LEN - 1,
13621243Sjkh      "Member groups:", "The groups this user belongs to (i.e. gets access rights for)",
13721243Sjkh      umemb, STRINGOBJ, NULL },
138186183Skensmith#define LAYOUT_HOMEDIR		7
139186183Skensmith    { 14, 6, 20, HOMEDIR_FIELD_LEN - 1,
14021243Sjkh      "Home directory:", "The user's home directory (leave blank for default)",
14121243Sjkh      homedir, STRINGOBJ, NULL },
142186183Skensmith#define LAYOUT_SHELL		8
143186183Skensmith    { 14, 29, 29, SHELL_FIELD_LEN - 1,
14421243Sjkh      "Login shell:", "The user's login shell (leave blank for default)",
14521243Sjkh      shell, STRINGOBJ, NULL },
146186183Skensmith#define LAYOUT_U_OKBUTTON	9
14721243Sjkh    { 18, 15, 0, 0,
14821243Sjkh      "OK", "Select this if you are happy with these settings",
14920231Sjkh	&okbutton, BUTTONOBJ, NULL },
150186183Skensmith#define LAYOUT_U_CANCELBUTTON	10
15121243Sjkh    { 18, 35, 0, 0,
15221243Sjkh      "CANCEL", "Select this if you wish to cancel this screen",
15321243Sjkh      &cancelbutton, BUTTONOBJ, NULL },
154156123Sjhb    LAYOUT_END,
15520231Sjkh};
15620231Sjkh
15720231Sjkh/* whine */
15820231Sjkhstatic void
15920231Sjkhfeepout(char *msg)
16020231Sjkh{
16120231Sjkh    beep();
16220231Sjkh    dialog_notify(msg);
16320231Sjkh}
16420231Sjkh
16520231Sjkh/* Check for the settings on the screen. */
16620231Sjkh
16720231Sjkhstatic int
16820231SjkhverifyGroupSettings(void)
16920231Sjkh{
17020231Sjkh    char tmp[256], *cp;
171195828Skensmith    unsigned long lgid;
17220231Sjkh
17320231Sjkh    if (strlen(gname) == 0) {
17420231Sjkh	feepout("The group name field must not be empty!");
17520231Sjkh	return 0;
17620231Sjkh    }
17720231Sjkh    snprintf(tmp, 256, "pw group show -q -n %s > /dev/null", gname);
17879304Skris    if (vsystem("%s", tmp) == 0) {
17920231Sjkh	feepout("This group name is already in use.");
18020231Sjkh	return 0;
18120231Sjkh    }
18220231Sjkh    if (strlen(gid) > 0) {
183195828Skensmith	lgid = strtoul(gid, &cp, 10);
184195828Skensmith	if (lgid == 0 || lgid > GID_MAX || (*cp != '\0' && !isspace(*cp))) {
185195828Skensmith	    feepout("The GID must be a number between 1 and 4294967295.");
18620231Sjkh	    return 0;
18720231Sjkh	}
18820231Sjkh    }
18920231Sjkh    if (strlen(gmemb) > 0) {
19020231Sjkh	if (strpbrk(gmemb, " \t") != NULL) {
19120231Sjkh	    feepout("The group member list must not contain any whitespace;\n"
19220231Sjkh		    "use commas to separate the names.");
19320231Sjkh	    return 0;
19420231Sjkh	}
19520231Sjkh#ifndef notyet  /* XXX */
19620255Sjoerg	feepout("Sorry, the group member list feature\n"
19720255Sjoerg		"is currently not yet implemented.");
19820231Sjkh	return 0;
19920231Sjkh#endif
20020231Sjkh    }
20120231Sjkh
20220231Sjkh    return 1;
20320231Sjkh}
20420231Sjkh
20520271Sjoerg/*
20620271Sjoerg * Ask pw(8) to fill in the blanks for us.
20720271Sjoerg * Works solely on the global variables.
20820271Sjoerg */
20920271Sjoerg
21020231Sjkhstatic void
21120271SjoergcompleteGroup(void)
21220271Sjoerg{
21320271Sjoerg    int pfd[2], i;
21420271Sjoerg    char tmp[256], *cp;
21520271Sjoerg    ssize_t l;
21620271Sjoerg    size_t amnt;
21720271Sjoerg    pid_t pid;
21820271Sjoerg    char *vec[4] =
21920271Sjoerg    {
22020271Sjoerg	"pw", "group", "next", 0
22120271Sjoerg    };
22220271Sjoerg
22320271Sjoerg    pipe (pfd);
22420271Sjoerg    if ((pid = fork()) == 0)
22520271Sjoerg    {
22620271Sjoerg	/* The kiddy. */
22720271Sjoerg	dup2(pfd[1], 1);
22820271Sjoerg	dup2(pfd[1], 2);
22920271Sjoerg	for (i = getdtablesize(); i > 2; i--)
23020271Sjoerg	    close(i);
23120271Sjoerg
23220271Sjoerg	execv("/usr/sbin/pw", vec);
23320271Sjoerg	msgDebug("Cannot execv() /usr/sbin/pw.\n");
23420271Sjoerg	_exit(99);
23520271Sjoerg    }
23620271Sjoerg    else
23720271Sjoerg    {
23820271Sjoerg	/* The oldie. */
23920271Sjoerg	close(pfd[1]);
24020271Sjoerg	amnt = sizeof tmp;
24120271Sjoerg	i = 0;
24220271Sjoerg	while((l = read(pfd[0], &tmp[i], amnt)) > 0)
24320271Sjoerg	{
24420271Sjoerg	    amnt -= l;
24520271Sjoerg	    i += l;
24620271Sjoerg	    if (amnt == 0)
24720271Sjoerg	    {
24820271Sjoerg		close(pfd[0]);
24920271Sjoerg		break;
25020271Sjoerg	    }
25120271Sjoerg	}
25220271Sjoerg	close(pfd[0]);
25320271Sjoerg	tmp[i] = '\0';
25420271Sjoerg	waitpid(pid, &i, 0);
25520271Sjoerg	if (WIFSIGNALED(i) || WEXITSTATUS(i) != 0)
25620271Sjoerg	    /* ignore by now */
25720271Sjoerg	    return;
25820271Sjoerg	if ((cp = strchr(tmp, '\n')) != NULL)
25920271Sjoerg	    *cp = '\0';
26020271Sjoerg	strncpy(gid, tmp, sizeof gid);
26120271Sjoerg    }
26220271Sjoerg}
26320271Sjoerg
26420271Sjoergstatic void
26520231SjkhaddGroup(WINDOW *ds_win)
26620231Sjkh{
26720231Sjkh    char tmp[256];
26820231Sjkh    int pfd[2], i;
26920231Sjkh    ssize_t l;
27020231Sjkh    size_t amnt;
27120231Sjkh    pid_t pid;
27220231Sjkh    char *vec[8] =
27320231Sjkh    {
27420231Sjkh	"pw", "group", "add", "-n", 0, "-g", 0, 0
27520231Sjkh    };
27620231Sjkh#define VEC_GNAME 4
27720231Sjkh#define VEC_GID 6
27820231Sjkh
27920231Sjkh    msgNotify("Adding group \"%s\"...", gname);
28020231Sjkh
28120231Sjkh    pipe (pfd);
28220231Sjkh    if ((pid = fork()) == 0)
28320231Sjkh    {
28420231Sjkh	/* The kiddy. */
28520231Sjkh	dup2(pfd[1], 1);
28620231Sjkh	dup2(pfd[1], 2);
28720231Sjkh	for (i = getdtablesize(); i > 2; i--)
28820231Sjkh	    close(i);
28920231Sjkh
29020231Sjkh	vec[VEC_GNAME] = gname;
29120231Sjkh
29220231Sjkh	if (strlen(gid) > 0)
29320231Sjkh	    vec[VEC_GID] = gid;
29420231Sjkh	else
29520231Sjkh	    vec[VEC_GID - 1] = 0;
29620231Sjkh
29720231Sjkh	execv("/usr/sbin/pw", vec);
29820231Sjkh	msgDebug("Cannot execv() /usr/sbin/pw.\n");
29920231Sjkh	_exit(99);
30020231Sjkh    }
30120231Sjkh    else
30220231Sjkh    {
30320231Sjkh	/* The oldie. */
30420231Sjkh	close(pfd[1]);
30520231Sjkh	amnt = sizeof tmp;
30620231Sjkh	i = 0;
30720231Sjkh	while((l = read(pfd[0], &tmp[i], amnt)) > 0)
30820231Sjkh	{
30920231Sjkh	    amnt -= l;
31020231Sjkh	    i += l;
31120231Sjkh	    if (amnt == 0)
31220231Sjkh	    {
31320231Sjkh		close(pfd[0]);
31420231Sjkh		break;
31520231Sjkh	    }
31620231Sjkh	}
31720231Sjkh	close(pfd[0]);
31820231Sjkh	tmp[i] = '\0';
31920231Sjkh	waitpid(pid, &i, 0);
32020231Sjkh	if (WIFSIGNALED(i))
32120231Sjkh	    msgDebug("pw(8) exited with signal %d.\n", WTERMSIG(i));
32220231Sjkh	else if(WEXITSTATUS(i))
32320231Sjkh	{
32420231Sjkh	    i = 0;
32520231Sjkh	    if(strncmp(tmp, "pw: ", 4) == 0)
32620231Sjkh		i = 4;
32720231Sjkh	    tmp[sizeof tmp - 1] = '\0';	/* sanity */
32820231Sjkh	    msgConfirm("The `pw' command exited with an error status.\n"
32920231Sjkh		       "Its error message was:\n\n%s",
33020231Sjkh		       &tmp[i]);
33120231Sjkh	}
33220231Sjkh    }
33320271Sjoerg#undef VEC_GNAME
33420271Sjoerg#undef VEC_GID
33520231Sjkh}
33620231Sjkh
33720231Sjkhint
33820231SjkhuserAddGroup(dialogMenuItem *self)
33920231Sjkh{
34020231Sjkh    WINDOW              *ds_win, *save;
34120231Sjkh    ComposeObj          *obj = NULL;
34221243Sjkh    int                 n = 0, cancel = FALSE, ret;
34321243Sjkh    int			max, firsttime = TRUE;
34420231Sjkh
34520231Sjkh    if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
34620231Sjkh        msgConfirm("This option may only be used after the system is installed, sorry!");
34720231Sjkh        return DITEM_FAILURE;
34820231Sjkh    }
34920231Sjkh
35020231Sjkh    save = savescr();
35120231Sjkh    dialog_clear_norefresh();
35220231Sjkh    /* We need a curses window */
35321243Sjkh    if (!(ds_win = openLayoutDialog(USER_HELPFILE, " User and Group Management ",
35421243Sjkh				    USER_DIALOG_X, USER_DIALOG_Y, USER_DIALOG_WIDTH, USER_DIALOG_HEIGHT))) {
35521243Sjkh	beep();
35621243Sjkh	msgConfirm("Cannot open addgroup dialog window!!");
35721243Sjkh	return(DITEM_FAILURE);
35821243Sjkh    }
35920231Sjkh
36021243Sjkh    /* Draw a group entry box */
36121701Sjkh    draw_box(ds_win, USER_DIALOG_Y + 2, USER_DIALOG_X + 8, USER_DIALOG_HEIGHT - 8,
36220231Sjkh	     USER_DIALOG_WIDTH - 17, dialog_attr, border_attr);
36320231Sjkh    wattrset(ds_win, dialog_attr);
36420231Sjkh    mvwaddstr(ds_win, USER_DIALOG_Y + 2, USER_DIALOG_X + 22, " Add a new group ");
36520231Sjkh
36620231Sjkh    CLEAR(gname);
36720231Sjkh    CLEAR(gid);
36820231Sjkh    CLEAR(gmemb);
36920231Sjkh
37021243Sjkh    /* Some more initialisation before we go into the main input loop */
37121243Sjkh    obj = initLayoutDialog(ds_win, groupLayout, USER_DIALOG_X, USER_DIALOG_Y, &max);
37220231Sjkh
37321243Sjkhreenter:
37421243Sjkh    cancelbutton = okbutton = 0;
37522099Sjkh    if (firsttime) {
37622099Sjkh	/* fill in the blanks, well, just the GID */
37722099Sjkh	completeGroup();
37822099Sjkh	RefreshStringObj(groupLayout[LAYOUT_GID].obj);
37922099Sjkh	firsttime = FALSE;
38020231Sjkh    }
38120231Sjkh
38222099Sjkh    while (layoutDialogLoop(ds_win, groupLayout, &obj, &n, max, &cancelbutton, &cancel));
38322099Sjkh
38421701Sjkh    if (!cancel && !verifyGroupSettings())
38521243Sjkh	goto reenter;
38620231Sjkh
38720231Sjkh    /* Clear this crap off the screen */
38823588Sjkh    delwin(ds_win);
38920231Sjkh    dialog_clear_norefresh();
39020231Sjkh    use_helpfile(NULL);
39120231Sjkh
39220231Sjkh    if (!cancel) {
39320231Sjkh	addGroup(ds_win);
39421243Sjkh	ret = DITEM_SUCCESS;
39520231Sjkh    }
39621243Sjkh    else
39721243Sjkh	ret = DITEM_FAILURE;
39820231Sjkh    restorescr(save);
39921243Sjkh    return ret;
40020231Sjkh}
40120231Sjkh
40220231Sjkh/* Check for the settings on the screen. */
40320231Sjkh
40420231Sjkhstatic int
40520231SjkhverifyUserSettings(WINDOW *ds_win)
40620231Sjkh{
40720231Sjkh    char tmp[256], *cp;
408195828Skensmith    unsigned long luid;
40920231Sjkh    WINDOW *save;
41020231Sjkh    int rv;
41120231Sjkh
41220231Sjkh    if (strlen(uname) == 0) {
41320231Sjkh	feepout("The user name field must not be empty!");
41420231Sjkh	return 0;
41520231Sjkh    }
41620271Sjoerg    snprintf(tmp, 256, "pw user show -q -n %s > /dev/null", uname);
41779304Skris    if (vsystem("%s", tmp) == 0) {
41820231Sjkh	feepout("This user name is already in use.");
41920231Sjkh	return 0;
42020231Sjkh    }
42120231Sjkh    if (strlen(uid) > 0) {
422195828Skensmith	luid = strtoul(uid, &cp, 10);
423195828Skensmith	if (luid == 0 || luid > UID_MAX || (*cp != '\0' && !isspace(*cp))) {
424195828Skensmith	    feepout("The UID must be a number between 1 and 4294967295.");
42520231Sjkh	    return 0;
42620231Sjkh	}
42720231Sjkh    }
428186183Skensmith    if (strcmp(passwd, confpasswd)) {
429186183Skensmith	feepout("Passwords don't match");
430186183Skensmith	return 0;
431186183Skensmith    }
43259089Smurray    if ((homedir[0]!=0) && (homedir[0]!='/')) {
43359089Smurray	feepout("The pathname for home directories must begin with a '/'.");
43459089Smurray	return 0;
43559089Smurray    }
43620231Sjkh    if (strlen(shell) > 0) {
437124687Scharnier	setusershell();
43820231Sjkh	while((cp = getusershell()) != NULL)
43920231Sjkh	    if (strcmp(cp, shell) == 0)
44020231Sjkh		break;
44120231Sjkh	endusershell();
44220231Sjkh	if (cp == NULL) {
44320231Sjkh	    save = savescr();
44420231Sjkh	    rv = msgYesNo("Warning:\n\n"
44520231Sjkh			  "The requested shell \"%s\" is not\n"
44620231Sjkh			  "a valid user shell.\n\n"
44720231Sjkh			  "Use it anyway?\n", shell);
44820231Sjkh	    restorescr(save);
44920231Sjkh	    wrefresh(ds_win);
45020231Sjkh	    if (rv != DITEM_SUCCESS)
45120231Sjkh		return 0;
45220231Sjkh	}
45320231Sjkh
45420231Sjkh    }
45520231Sjkh
45620231Sjkh    if (strlen(umemb) > 0) {
45720231Sjkh	if (strpbrk(umemb, " \t") != NULL) {
45820231Sjkh	    feepout("The member groups list must not contain any whitespace;\n"
45920231Sjkh		    "use commas to separate the names.");
46020231Sjkh	    return 0;
46120231Sjkh	}
46220231Sjkh    }
46320231Sjkh
46420231Sjkh    return 1;
46520231Sjkh}
46620231Sjkh
46720271Sjoerg/*
46820271Sjoerg * Ask pw(8) to fill in the blanks for us.
46920271Sjoerg * Works solely on the global variables.
47020271Sjoerg */
47120271Sjoerg
47220231Sjkhstatic void
47320271SjoergcompleteUser(void)
47420231Sjkh{
47520231Sjkh    int pfd[2], i;
47620271Sjoerg    char tmp[256], *cp, *cp2;
47720231Sjkh    ssize_t l;
47820231Sjkh    size_t amnt;
47920231Sjkh    pid_t pid;
48020271Sjoerg    char *vec[7] =
48120271Sjoerg    {
48220271Sjoerg	"pw", "user", "add", "-N", "-n", 0, 0
48320271Sjoerg    };
48420271Sjoerg#define VEC_UNAME 5
48520271Sjoerg
48620271Sjoerg    pipe (pfd);
48720271Sjoerg    if ((pid = fork()) == 0)
48820271Sjoerg    {
48920271Sjoerg	/* The kiddy. */
49020271Sjoerg	dup2(pfd[1], 1);
49120271Sjoerg	dup2(pfd[1], 2);
49220271Sjoerg	for (i = getdtablesize(); i > 2; i--)
49320271Sjoerg	    close(i);
49420271Sjoerg
49520271Sjoerg	vec[VEC_UNAME] = uname;
49620271Sjoerg
49720271Sjoerg	execv("/usr/sbin/pw", vec);
49820271Sjoerg	msgDebug("Cannot execv() /usr/sbin/pw.\n");
49920271Sjoerg	_exit(99);
50020271Sjoerg    }
50120271Sjoerg    else
50220271Sjoerg    {
50320271Sjoerg	/* The oldie. */
50420271Sjoerg	close(pfd[1]);
50520271Sjoerg	amnt = sizeof tmp;
50620271Sjoerg	i = 0;
50720271Sjoerg	while((l = read(pfd[0], &tmp[i], amnt)) > 0)
50820271Sjoerg	{
50920271Sjoerg	    amnt -= l;
51020271Sjoerg	    i += l;
51120271Sjoerg	    if (amnt == 0)
51220271Sjoerg	    {
51320271Sjoerg		close(pfd[0]);
51420271Sjoerg		break;
51520271Sjoerg	    }
51620271Sjoerg	}
51720271Sjoerg	close(pfd[0]);
51820271Sjoerg	tmp[i] = '\0';
51920271Sjoerg	waitpid(pid, &i, 0);
52020271Sjoerg	if (WIFSIGNALED(i) || WEXITSTATUS(i) != 0)
52120271Sjoerg	    /* ignore by now */
52220271Sjoerg	    return;
52320271Sjoerg	if ((cp = strchr(tmp, '\n')) != NULL)
52420271Sjoerg	    *cp = '\0';
52520271Sjoerg	if ((cp = strchr(tmp, ':')) == NULL || (cp = strchr(++cp, ':')) == NULL)
52620271Sjoerg	    return;
52720271Sjoerg	cp++;
52820271Sjoerg	if ((cp2 = strchr(cp, ':')) == NULL)
52920271Sjoerg	    return;
53020271Sjoerg	*cp2++ = '\0';
53120271Sjoerg	strncpy(uid, cp, sizeof uid);
53220271Sjoerg	cp = cp2;
53320271Sjoerg	if ((cp2 = strchr(cp, ':')) == NULL)
53420271Sjoerg	    return;
53520271Sjoerg	*cp2++ = '\0';
53620271Sjoerg#ifdef notyet /* XXX pw user add -g doesn't accept a numerical GID */
53720271Sjoerg	strncpy(ugroup, cp, sizeof ugroup);
53820271Sjoerg#endif
53920271Sjoerg	cp = cp2;
54020271Sjoerg	if ((cp2 = strchr(cp, ':')) == NULL || (cp2 = strchr(++cp2, ':')) == NULL ||
54120271Sjoerg	    (cp = cp2 = strchr(++cp2, ':')) == NULL || (cp2 = strchr(++cp2, ':')) == NULL)
54220271Sjoerg	    return;
54320271Sjoerg	*cp2++ = '\0';
54420271Sjoerg	cp++;
54520271Sjoerg	strncpy(gecos, cp, sizeof gecos);
54620271Sjoerg	cp = cp2;
54720271Sjoerg	if ((cp2 = strchr(cp, ':')) == NULL)
54820271Sjoerg	    return;
54920271Sjoerg	*cp2++ = '\0';
55020271Sjoerg	if (*cp2)
55120271Sjoerg	    strncpy(shell, cp2, sizeof shell);
55220271Sjoerg    }
55320271Sjoerg#undef VEC_UNAME
55420271Sjoerg}
55520271Sjoerg
55620271Sjoergstatic void
55720271SjoergaddUser(WINDOW *ds_win)
55820271Sjoerg{
55920271Sjoerg    char tmp[256], *msg;
56020484Sjkh    int pfd[2], ipfd[2], i, j;
56120271Sjoerg    ssize_t l;
56220271Sjoerg    size_t amnt;
56320271Sjoerg    pid_t pid;
56420231Sjkh    /*
56520231Sjkh     * Maximal list:
56620504Sjoerg     * pw user add -m -n uname -g grp -u uid -c comment -d homedir -s shell -G grplist -h 0
56720231Sjkh     */
56820504Sjoerg    char *vec[21] =
56920231Sjkh    {
57020231Sjkh	"pw", "user", "add", "-m", "-n", /* ... */
57120231Sjkh    };
57220231Sjkh#define VEC_UNAME 5
57320231Sjkh
57420231Sjkh    msgNotify("Adding user \"%s\"...", uname);
57520231Sjkh
57620231Sjkh    pipe (pfd);
57720484Sjkh    pipe (ipfd);
57820231Sjkh    if ((pid = fork()) == 0)
57920231Sjkh    {
58020231Sjkh	/* The kiddy. */
58120484Sjkh	dup2(ipfd[0], 0);
58220231Sjkh	dup2(pfd[1], 1);
58320231Sjkh	dup2(pfd[1], 2);
58420231Sjkh	for (i = getdtablesize(); i > 2; i--)
58520231Sjkh	    close(i);
58620231Sjkh
58720231Sjkh	vec[i = VEC_UNAME] = uname;
58820231Sjkh	i++;
58920231Sjkh#define ADDVEC(var, option) do { if (strlen(var) > 0) { vec[i++] = option; vec[i++] = var; } } while (0)
59020271Sjoerg	ADDVEC(ugroup, "-g");
59120231Sjkh	ADDVEC(uid, "-u");
59220231Sjkh	ADDVEC(gecos, "-c");
59320271Sjoerg	ADDVEC(homedir, "-d");
59420231Sjkh	ADDVEC(shell, "-s");
59520231Sjkh	ADDVEC(umemb, "-G");
59620484Sjkh	if (passwd[0]) {
59720484Sjkh	    vec[i++] = "-h";
59820484Sjkh	    vec[i++] = "0";
59920484Sjkh	}
60020231Sjkh	vec[i] = 0;
60120231Sjkh
60220231Sjkh	execv("/usr/sbin/pw", vec);
60320231Sjkh	msgDebug("Cannot execv() /usr/sbin/pw.\n");
60420231Sjkh	_exit(99);
60520231Sjkh    }
60620231Sjkh    else
60720231Sjkh    {
60820231Sjkh	/* The oldie. */
60920231Sjkh	close(pfd[1]);
61020484Sjkh	close(ipfd[0]);
61120484Sjkh
61220484Sjkh	if (passwd[0])
61320484Sjkh	    write(ipfd[1], passwd, strlen(passwd));
61420484Sjkh	close(ipfd[1]);
61520231Sjkh	amnt = sizeof tmp;
61620231Sjkh	i = 0;
61720231Sjkh	while((l = read(pfd[0], &tmp[i], amnt)) > 0)
61820231Sjkh	{
61920231Sjkh	    amnt -= l;
62020231Sjkh	    i += l;
62120231Sjkh	    if (amnt == 0)
62220231Sjkh	    {
62320231Sjkh		close(pfd[0]);
62420231Sjkh		break;
62520231Sjkh	    }
62620231Sjkh	}
62720231Sjkh	close(pfd[0]);
62820231Sjkh	tmp[i] = '\0';
62920231Sjkh	waitpid(pid, &i, 0);
63020231Sjkh	if (WIFSIGNALED(i))
63120231Sjkh	{
63220271Sjoerg	    j = WTERMSIG(i);
63320271Sjoerg	    msg = "The `pw' command exited with signal %d.\n";
63420271Sjoerg	    goto sysfail;
63520271Sjoerg	}
63620271Sjoerg	else if((j = WEXITSTATUS(i)))
63720271Sjoerg	{
63820231Sjkh	    i = 0;
63920231Sjkh	    if(strncmp(tmp, "pw: ", 4) == 0)
64020231Sjkh		i = 4;
64120231Sjkh	    tmp[sizeof tmp - 1] = '\0';	/* sanity */
64220271Sjoerg	    if (j == EX_DATAERR || j == EX_NOUSER || j == EX_SOFTWARE)
64320271Sjoerg		msgConfirm("The `pw' command exited with an error status.\n"
64420271Sjoerg			   "Its error message was:\n\n%s",
64520271Sjoerg			   &tmp[i]);
64620271Sjoerg	    else
64720271Sjoerg	    {
64820271Sjoerg		msg = "The `pw' command exited with unexpected status %d.\n";
64920271Sjoerg	sysfail:
65020271Sjoerg		msgDebug(msg, j);
65120271Sjoerg		msgDebug("Command stdout and stderr was:\n\n%s", tmp);
65220271Sjoerg		msgConfirm(msg, j);
65320271Sjoerg	    }
65420231Sjkh	}
65520484Sjkh	else if (!passwd[0])
65620231Sjkh	    msgConfirm("You will need to enter a password for this user\n"
65720231Sjkh		       "later, using the passwd(1) command from the shell.\n\n"
65820231Sjkh		       "The account for `%s' is currently still disabled.",
65920231Sjkh		       uname);
66020231Sjkh    }
66120271Sjoerg#undef VEC_UNAME
66220271Sjoerg#undef ADDVEC
66320231Sjkh}
66420231Sjkh
66520231Sjkhint
66620231SjkhuserAddUser(dialogMenuItem *self)
66720231Sjkh{
66820231Sjkh    WINDOW              *ds_win, *save;
66920231Sjkh    ComposeObj          *obj = NULL;
67021243Sjkh    int                 n = 0, cancel = FALSE, ret;
67159089Smurray    int			max, firsttime = TRUE, filled=0;
67220231Sjkh
67320231Sjkh    if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
67420231Sjkh        msgConfirm("This option may only be used after the system is installed, sorry!");
67520231Sjkh        return DITEM_FAILURE;
67620231Sjkh    }
67720231Sjkh
67820231Sjkh    save = savescr();
67920231Sjkh    dialog_clear_norefresh();
68021243Sjkh
68120231Sjkh    /* We need a curses window */
68221243Sjkh    if (!(ds_win = openLayoutDialog(USER_HELPFILE, " User and Group Management ",
68321243Sjkh				    USER_DIALOG_X, USER_DIALOG_Y, USER_DIALOG_WIDTH, USER_DIALOG_HEIGHT))) {
68421243Sjkh	beep();
68521243Sjkh	msgConfirm("Cannot open adduser dialog window!!");
68621243Sjkh	return(DITEM_FAILURE);
68721243Sjkh    }
68820231Sjkh
68921243Sjkh    /* Draw a user entry box */
69021701Sjkh    draw_box(ds_win, USER_DIALOG_Y + 1, USER_DIALOG_X + 3, USER_DIALOG_HEIGHT - 6,
69120231Sjkh	     USER_DIALOG_WIDTH - 6, dialog_attr, border_attr);
69220231Sjkh    wattrset(ds_win, dialog_attr);
693186183Skensmith    mvwaddstr(ds_win, USER_DIALOG_Y + 1, USER_DIALOG_X + 24, " Add a new user ");
69420231Sjkh
69520231Sjkh    CLEAR(uname);
69620231Sjkh    CLEAR(uid);
69720231Sjkh    CLEAR(ugroup);
69820231Sjkh    CLEAR(gecos);
69920484Sjkh    CLEAR(passwd);
700189955Sganbold    CLEAR(confpasswd);
70120231Sjkh    CLEAR(umemb);
70220231Sjkh    CLEAR(homedir);
70320231Sjkh    CLEAR(shell);
70420231Sjkh
70520231Sjkh    /* Some more initialisation before we go into the main input loop */
70621243Sjkh    obj = initLayoutDialog(ds_win, userLayout, USER_DIALOG_X, USER_DIALOG_Y, &max);
70721243Sjkh
70821243Sjkhreenter:
70920231Sjkh    cancelbutton = okbutton = 0;
71022099Sjkh    if (firsttime) {
71122099Sjkh	/* fill in the blanks, well, just the GID */
71222099Sjkh	completeUser();
71322099Sjkh	RefreshStringObj(userLayout[LAYOUT_UID].obj);
71422099Sjkh	RefreshStringObj(userLayout[LAYOUT_UGROUP].obj);
71522099Sjkh	RefreshStringObj(userLayout[LAYOUT_GECOS].obj);
71622099Sjkh	RefreshStringObj(userLayout[LAYOUT_UMEMB].obj);
71722099Sjkh	RefreshStringObj(userLayout[LAYOUT_HOMEDIR].obj);
71822099Sjkh	RefreshStringObj(userLayout[LAYOUT_SHELL].obj);
71922099Sjkh	firsttime = FALSE;
72020231Sjkh    }
72120231Sjkh
72259089Smurray    while (layoutDialogLoop(ds_win, userLayout, &obj, &n, max, &cancelbutton, &cancel)) {
72359089Smurray	/* Prevent this from being irritating if user really means NO */
72459089Smurray	if (filled < 3) {
72559089Smurray	  if ((uname[0]) && !homedir[0]) {
72659089Smurray	      SAFE_STRCPY(homedir,"/home/");
72759089Smurray	      strcat(homedir,uname);
72859089Smurray	      RefreshStringObj(userLayout[LAYOUT_HOMEDIR].obj);
72959089Smurray	      ++filled;
73059089Smurray	    }
73159089Smurray	}
73259089Smurray    };
73322099Sjkh
73421701Sjkh    if (!cancel && !verifyUserSettings(ds_win))
73521243Sjkh	goto reenter;
73621243Sjkh
73720231Sjkh    /* Clear this crap off the screen */
73823588Sjkh    delwin(ds_win);
73920231Sjkh    dialog_clear_norefresh();
74020231Sjkh    use_helpfile(NULL);
74120231Sjkh
74220231Sjkh    if (!cancel) {
74320231Sjkh	addUser(ds_win);
74421243Sjkh	ret = DITEM_SUCCESS;
74520231Sjkh    }
74621243Sjkh    else
74721243Sjkh	ret = DITEM_FAILURE;
74820231Sjkh    restorescr(save);
74921243Sjkh    return ret;
75020231Sjkh}
75120231Sjkh
752