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