📄 usermod.c
字号:
/* Copyright (C) 2003, 2004, 2005 Thorsten Kukuk Author: Thorsten Kukuk <kukuk@suse.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#ifdef HAVE_CONFIG_H#include "config.h"#endif#define _GNU_SOURCE#include <time.h>#include <utmp.h>#include <fcntl.h>#include <paths.h>#include <ctype.h>#include <errno.h>#include <stdio.h>#include <getopt.h>#include <unistd.h>#include <string.h>#include <signal.h>#include <libgen.h>#include <sys/resource.h>#ifdef HAVE_LIBNSCD_H#include <libnscd.h>#endif#ifdef USE_LDAP#include "libldap.h"#endif#include "i18n.h"#include "public.h"#include "group.h"#include "logging.h"#include "utf8conv.h"#include "logindefs.h"#include "read-files.h"#include "error_codes.h"static voidprint_usage (FILE * stream, const char *program){ fprintf (stream, _("Usage: %s ...\n"), program);}static voidprint_help (const char *program){ print_usage (stdout, program); fprintf (stdout, _("%s - modify a user account\n\n"), program); fputs (_(" -c comment Set the GECOS field for the new account\n"), stdout); fputs (_ (" -D binddn Use dn \"binddn\" to bind to the LDAP directory\n"), stdout); fputs (_(" -d homedir Home directory for the new user\n"), stdout); fputs (_(" -e expire Date on which the new account will be disabled\n"), stdout); fputs (_(" -f inactive Days after a password expires until account is \disabled\n"), stdout); fputs (_(" -G group,... List of supplementary groups\n"), stdout); fputs (_(" -g gid Name/number of the users primary group\n"), stdout); fputs (_(" -l login Change login name.\n"), stdout); fputs (_(" -m Move home directory to the new path\n"), stdout); fputs (_(" -o Allow duplicate (non-unique) UID\n"), stdout); fputs (_(" -A group,... List of groups the user should be added to\n"), stdout); fputs (_(" -R group,... List of groups the user should be removed from\n"), stdout); fputs (_(" -P path Search passwd, shadow and group file in \"path\"\n"), stdout); fputs (_(" -p password Encrypted password as returned by crypt(3)\n"), stdout); fputs (_(" -s shell Name of the user's login shell\n"), stdout); fputs (_(" -u uid Change the userid to the given number\n"), stdout); fputs (_(" --service srv Use nameservice 'srv'\n"), stdout); fputs (_(" -L Locks the password entry for \"user\"\n"), stdout); fputs (_(" -U Try to unlock the password entry for \"user\"\n"), stdout); fputs (_(" --help Give this help list\n"), stdout); fputs (_(" --usage Give a short usage message\n"), stdout); fputs (_(" -v, --version Print program version\n"), stdout); fputs (_("Valid services are: files, ldap\n"), stdout);}static const char *program = "usermod";static struct option long_options[] = { {"comment", required_argument, NULL, 'c'}, {"gecos", required_argument, NULL, 'c'},#ifdef USE_LDAP {"binddn", required_argument, NULL, 'D'},#endif {"home", required_argument, NULL, 'd'}, {"expire", required_argument, NULL, 'e'}, {"inactive", required_argument, NULL, 'f'}, {"groups", required_argument, NULL, 'G'}, {"add-to-groups", required_argument, NULL, 'A'}, {"remove-from-groups", required_argument, NULL, 'R'}, {"gid", required_argument, NULL, 'g'}, {"login", required_argument, NULL, 'l'}, {"lock", required_argument, NULL, 'L'}, {"move-home", no_argument, NULL, 'm'}, {"non-unique", no_argument, NULL, 'o'}, {"path", required_argument, NULL, 'P'}, {"password", required_argument, NULL, 'p'}, {"shell", required_argument, NULL, 's'}, {"uid", required_argument, NULL, 'u'}, {"unlock", required_argument, NULL, 'U'}, {"version", no_argument, NULL, 'v'}, {"service", required_argument, NULL, '\253'}, {"usage", no_argument, NULL, '\254'}, {"help", no_argument, NULL, '\255'}, {NULL, 0, NULL, '\0'}};static const char *short_options = "A:c:D:d:e:f:G:g:l:LmoP:p:R:s:u:Uv";/* This function converts a comma seperated list of groups (a group can be a groupname or a group id) into a table of group ids. It returns 0 on success. */static intconvert_grpopt_to_name (const char *arg, char **grouplist, gid_t *groupid, const char *use_service){ group_t *gr_data; if (isdigit (*arg)) { gid_t gid; if (strtoid (arg, &gid) == -1) /* invalid number */ { fprintf (stderr, _("%s: Invalid numeric argument `%s'.\n"), program, arg); return E_BAD_ARG; } gr_data = find_group_data (NULL, gid, use_service); if (gr_data == NULL || gr_data->service == S_NONE) { if (use_service) { fprintf (stderr, _("%s: Group `%u' not found in service `%s'.\n"), program, gid, use_service); return E_NOTFOUND; } else { fprintf (stderr, _("%s: Unknown group `%u'.\n"), program, gid); return E_BAD_ARG; } } } else { gr_data = find_group_data (arg, 0, use_service); if (gr_data == NULL || gr_data->service == S_NONE) { if (use_service) { fprintf (stderr, _("%s: Group `%s' not found in service `%s'.\n"), program, utf8_to_locale (arg), use_service); return E_NOTFOUND; } else { fprintf (stderr, _("%s: Unknown group `%s'.\n"), program, utf8_to_locale (arg)); return E_BAD_ARG; } } } if (grouplist) *grouplist = strdup (gr_data->gr.gr_name); if (groupid) *groupid = gr_data->gr.gr_gid; return 0;}static struct passwd *files_getpwnam (const char *name){ enum nss_status status; static int buflen = 256; static char *buffer = NULL; static struct passwd resultbuf; if (buffer == NULL) buffer = malloc (buflen); while ((status = files_getpwnam_r (name, &resultbuf, buffer, buflen, &errno)) == NSS_STATUS_TRYAGAIN && errno == ERANGE) { errno = 0; buflen += 256; buffer = realloc (buffer, buflen); } if (status == NSS_STATUS_SUCCESS) return &resultbuf; else return NULL;}static struct passwd *files_getpwuid (uid_t uid){ enum nss_status status; static int buflen = 256; static char *buffer = NULL; static struct passwd resultbuf; if (buffer == NULL) buffer = malloc (buflen); while ((status = files_getpwuid_r (uid, &resultbuf, buffer, buflen, &errno)) == NSS_STATUS_TRYAGAIN && errno == ERANGE) { errno = 0; buflen += 256; buffer = realloc (buffer, buflen); } if (status == NSS_STATUS_SUCCESS) return &resultbuf; else return NULL;}static struct group *files_getgrent (void){ enum nss_status status; static int buflen = 256; static char *buffer = NULL; static struct group resultbuf; if (buffer == NULL) buffer = malloc (buflen); while ((status = files_getgrent_r (&resultbuf, buffer, buflen, &errno)) == NSS_STATUS_TRYAGAIN && errno == ERANGE) { errno = 0; buflen += 256; buffer = realloc (buffer, buflen); } if (status == NSS_STATUS_SUCCESS) return &resultbuf; else return NULL;}static char **add_gr_mem (const char *name, char **gr_mem){ char **groups; unsigned int i; int already_added = 0; i = 0; while (gr_mem[i]) { if (strcmp (gr_mem[i], name) == 0) already_added = 1; ++i; } ++i; /* for trailing NULL pointer */ if (!already_added) ++i; groups = malloc (i * sizeof (char *)); i = 0; while (gr_mem[i]) { groups[i] = strdup (gr_mem[i]); ++i; } if (!already_added) { groups[i] = strdup (name); ++i; } groups[i] = NULL; return groups;}static char **rename_gr_mem (const char *name, char **gr_mem, const char *new_name){ char **groups; unsigned int i; for (i = 0; gr_mem[i]; i++) ; ++i; /* for trailing NULL pointer */ groups = malloc ((i + 1) * sizeof (char *)); if (groups == NULL) return NULL; for (i = 0; gr_mem[i]; i++) { if (strcmp (name, gr_mem[i]) != 0) groups[i] = strdup (gr_mem[i]); else groups[i] = strdup (new_name); } groups[i] = NULL; return groups;}static intrename_in_secondary_groups (user_t *pw_data, int have_extrapath){ struct item_t { char *value; struct item_t *next; } *list = NULL, *item; struct group *gr; int retval = E_SUCCESS; if (have_extrapath) { while ((gr = files_getgrent ())) { unsigned int i; for (i = 0; gr->gr_mem[i]; i++) { if (strcmp (gr->gr_mem[i], pw_data->pw.pw_name) == 0) { item = malloc (sizeof (*item)); item->value = strdup (gr->gr_name); item->next = list; list = item; } } } } else { setgrent (); while ((gr = getgrent ())) { unsigned int i; for (i = 0; gr->gr_mem[i]; i++) { if (strcmp (gr->gr_mem[i], pw_data->pw.pw_name) == 0) { item = malloc (sizeof (*item)); item->value = strdup (gr->gr_name); item->next = list; list = item; } } } endgrent (); } item = list; while (item != NULL) { group_t *gr_data = find_group_data (item->value, 0, NULL); if (gr_data == NULL || gr_data->service == S_NONE) { fprintf (stderr, _("%s: ERROR: Cannot find group `%s' anymore!\n"), program, utf8_to_locale (item->value)); if (retval == E_SUCCESS) retval = E_NOTFOUND; } else { gr_data->todo = DO_MODIFY;#ifdef USE_LDAP if (gr_data->service == S_LDAP) { if (pw_data->binddn == NULL) { sec_log (program, MSG_ERROR_RENAME_USER_IN_GROUP, pw_data->pw.pw_name, pw_data->pw.pw_uid, gr_data->gr.gr_name, gr_data->gr.gr_gid, getuid ()); fprintf (stderr, _("%s: User not renamed in LDAP group `%s'.\n"), program, utf8_to_locale (gr_data->gr.gr_name)); item = item->next; free_group_t (gr_data); retval = E_GRP_UPDATE; continue; } gr_data->binddn = strdup (pw_data->binddn); if (pw_data->oldclearpwd == NULL) { sec_log (program, MSG_ERROR_RENAME_USER_IN_GROUP, pw_data->pw.pw_name, pw_data->pw.pw_uid, gr_data->gr.gr_name, gr_data->gr.gr_gid, getuid ()); fprintf (stderr, _("%s: User not renamed from LDAP group `%s'.\n"), program, utf8_to_locale (gr_data->gr.gr_name)); item = item->next; free_group_t (gr_data); retval = E_GRP_UPDATE; continue; } }#endif if (pw_data->oldclearpwd) gr_data->oldclearpwd = strdup (pw_data->oldclearpwd); gr_data->new_gr_mem = rename_gr_mem (pw_data->pw.pw_name, gr_data->gr.gr_mem, pw_data->new_name); if (write_group_data (gr_data, 1) != 0) { sec_log (program, MSG_ERROR_RENAME_USER_IN_GROUP, pw_data->pw.pw_name, pw_data->pw.pw_uid, gr_data->gr.gr_name, gr_data->gr.gr_gid, getuid ()); fprintf (stderr, _("%s: User not renamed in group `%s'.\n"), program, utf8_to_locale (gr_data->gr.gr_name)); retval = E_GRP_UPDATE; } else { sec_log (program, MSG_USER_RENAMED_IN_GROUP, pw_data->new_name, pw_data->pw.pw_name, pw_data->pw.pw_uid, gr_data->gr.gr_name, gr_data->gr.gr_gid, getuid ()); } item = item->next; } free_group_t (gr_data); } return retval;}/* Move the users home directory to new location. */static intmove_home_directory (const char *oldhome, const char *newhome){ struct stat st; if (oldhome == NULL || *oldhome == '\0' || newhome == NULL || *newhome == '\0') return E_HOMEDIR; /* Does the old directory exist? */ if (stat (oldhome, &st) < 0) return 0; /* No old homedirectory, but no error, too. */ /* Don't try to move it if it is not a directory. Some admins have the bad idea to use a file as home directory. */ if (!S_ISDIR (st.st_mode)) return E_HOMEDIR; if (access (newhome, F_OK) == 0) return E_HOMEDIR; else { char path[strlen (newhome) + 2]; char *bhome, *cp; path[0] = '\0'; bhome = strdup (newhome); ++bhome; /* Check for every part of the path, if the directory exists. If not, create it with permissions 755 and owner root:root. */ cp = strtok (bhome, "/"); while (cp) { strcat (path, "/"); strcat (path, cp); if (access (path, F_OK) != 0) { if (mkdir (path, 0) != 0) { fprintf (stderr, _("%s: Cannot create directory `%s'.\n"), program, path); return E_HOMEDIR; } if (chown (path, 0, 0) < 0) fprintf (stderr, _("%s: Warning: chown on `%s' failed: %m\n"), program, path); if (chmod (path, 0755) < 0) fprintf (stderr, _("%s: Warning: chmod on `%s' failed: %m\n"), program, path); } cp = strtok (NULL, "/"); } /* we have this created to much, remove it again. */ rmdir (newhome); } if (rename (oldhome, newhome) == -1) { if (errno == EXDEV) { if (mkdir (newhome, st.st_mode & 0777)) { fprintf (stderr, _("Can't create `%s': %m\n"), newhome); return E_HOMEDIR; } if (chown (newhome, st.st_uid, st.st_gid)) { fprintf (stderr, _("%s: Warning: chown on `%s' failed: %m\n"), program, newhome); rmdir (newhome); return E_HOMEDIR; } if (copy_dir_rec (oldhome, newhome, 1, 0, 0) == 0) remove_dir_rec (oldhome); /* only remove if no error occured. */ else { fprintf (stderr, _("%s: Cannot copy directory %s to %s.\n"), program, oldhome, newhome); return E_HOMEDIR; } } else { fprintf (stderr, _("%s: Cannot rename directory %s to %s.\n"), program, oldhome, newhome); return E_HOMEDIR; } } return 0;}/* XXX Make this generic and put it into libpwdutils. */static intremove_from_secondary_groups (user_t *pw_data, int have_extrapath, const char *name){ struct item_t { char *value; struct item_t *next; } *list = NULL, *item; struct group *gr; int retval = E_SUCCESS; if (have_extrapath) { while ((gr = files_getgrent ())) { unsigned int i;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -