In this section, we look at library functions that permit us to retrieve individual records from the password, shadow password, and group files, and to scan all of the records in each of these files.
The getpwnam() and getpwuid() functions retrieve records from the password file.
#include <pwd.h> struct passwd *getpwnam
(const char *name); struct passwd *getpwuid
(uid_t uid);
Both return a pointer on success, or NULL
on error; see main text for description of the “not found” case
Given a login name in name, the getpwnam() function returns a pointer to a structure of the following type, containing the corresponding information from the password record:
struct passwd { char *pw_name; /* Login name (username) */ char *pw_passwd; /* Encrypted password */ uid_t pw_uid; /* User ID */ gid_t pw_gid; /* Group ID */ char *pw_gecos; /* Comment (user information) */ char *pw_dir; /* Initial working (home) directory */ char *pw_shell; /* Login shell */ };
The pw_gecos and pw_passwd fields of the passwd structure are not defined in SUSv3, but are available on all UNIX implementations. The pw_passwd field contains valid information only if password shadowing is not enabled. (Programmatically, the simplest way to determine whether password shadowing is enabled is to follow a successful getpwnam() call with a call to getspnam(), described shortly, to see if it returns a shadow password record for the same username.) Some other implementations provide additional, nonstandard fields in this structure.
The pw_gecos field derives its name from early UNIX implementations, where this field contained information that was used for communicating with a machine running the General Electric Comprehensive Operating System (GECOS). Although this usage has long since become obsolete, the field name has survived, and the field is used for recording information about the user.
The getpwuid() function returns exactly the same information as getpwnam(), but does a lookup using the numeric user ID supplied in the argument uid.
Both getpwnam() and getpwuid() return a pointer to a statically allocated structure. This structure is overwritten on each call to either of these functions (or to the getpwent() function described below).
Because they return a pointer to statically allocated memory, getpwnam() and getpwuid() are not reentrant. In fact, the situation is even more complex, since the returned passwd structure contains pointers to other information (e.g., the pw_name field) that is also statically allocated. (We explain reentrancy in Reentrant and Async-Signal-Safe Functions.) Similar statements apply to the getgrnam() and getgrgid() functions (described shortly).
SUSv3 specifies an equivalent set of reentrant functions—getpwnam_r(), getpwuid_r(), getgrnam_r(), and getgrgid_r()—that include as arguments both a passwd (or group) structure and a buffer area to hold the other structures to which the fields of the passwd (group) structure point. The number of bytes required for this additional buffer can be obtained using the call sysconf(_SC_GETPW_R_SIZE_MAX) (or sysconf(_SC_GETGR_R_SIZE_MAX) in the case of the group-related functions). See the manual pages for details of these functions.
According to SUSv3, if a matching passwd record can’t be found, then getpwnam() and getpwuid() should return NULL
and leave errno unchanged. This means that we should be able to distinguish the error and the “not found” cases using code such as the following:
struct passwd *pwd; errno = 0; pwd = getpwnam(name); if (pwd == NULL) { if (errno == 0) /* Not found */; else /* Error */; }
However, a number of UNIX implementations don’t conform to SUSv3 on this point. If a matching passwd record is not found, then these functions return NULL
and set errno to a nonzero value, such as ENOENT
or ESRCH
. Before version 2.7, glibc produced the error ENOENT
for this case, but since version 2.7, glibc conforms to the SUSv3 requirements. This variation across implementations arises in part because POSIX.1-1990 did not require these functions to set errno on error and allowed them to set errno for the “not found” case. The upshot of all of this is that it isn’t really possible to portably distinguish the error and “not found” cases when using these functions.
The getgrnam() and getgrgid() functions retrieve records from the group file.
#include <grp.h> struct group *getgrnam
(const char *name); struct group *getgrgid
(gid_t gid);
Both return a pointer on success, or NULL
on error; see main text for description of the “not found” case
The getgrnam() function looks up group information by group name, and the getgrgid() function performs lookups by group ID. Both functions return a pointer to a structure of the following type:
struct group { char *gr_name; /* Group name */ char *gr_passwd; /* Encrypted password (if not password shadowing) */ gid_t gr_gid; /* Group ID */ char **gr_mem; /* NULL-terminated array of pointers to names of members listed in /etc/group */ };
The gr_passwd field of the group structure is not specified in SUSv3, but is available on most UNIX implementations.
As with the corresponding password functions described above, this structure is overwritten on each call to one of these functions.
If these functions can’t find a matching group record, then they show the same variations in behavior that we described for getpwnam() and getpwuid().
One common use of the functions that we have already described in this section is to convert symbolic user and group names into numeric IDs and vice versa. Example 8-1 demonstrates these conversions, in the form of four functions: userNameFromId(), userIdFromName(), groupNameFromId(), and groupIdFromName(). As a convenience to the caller, userIdFromName() and groupIdFromName() also allow the name argument to be a (purely) numeric string; in that case, the string is converted directly to a number and returned to the caller. We employ these functions in some example programs later in this book.
Example 8-1. Functions to convert user and group IDs to and from user and group names
users_groups/ugid_functions.c
#include <pwd.h> #include <grp.h> #include <ctype.h> #include "ugid_functions.h" /* Declares functions defined here */ char * /* Return name corresponding to 'uid', or NULL on error */ userNameFromId(uid_t uid) { struct passwd *pwd; pwd = getpwuid(uid); return (pwd == NULL) ? NULL : pwd->pw_name; } uid_t /* Return UID corresponding to 'name', or -1 on error */ userIdFromName(const char *name) { struct passwd *pwd; uid_t u; char *endptr; if (name == NULL || *name == '\0') /* On NULL or empty string */ return -1; /* return an error */ u = strtol(name, &endptr, 10); /* As a convenience to caller */ if (*endptr == '\0') /* allow a numeric string */ return u; pwd = getpwnam(name); if (pwd == NULL) return -1; return pwd->pw_uid; } char * /* Return name corresponding to 'gid', or NULL on error */ groupNameFromId(gid_t gid) { struct group *grp; grp = getgrgid(gid); return (grp == NULL) ? NULL : grp->gr_name; } gid_t /* Return GID corresponding to 'name', or -1 on error */ groupIdFromName(const char *name) { struct group *grp; gid_t g; char *endptr; if (name == NULL || *name == '\0') /* On NULL or empty string */ return -1; /* return an error */ g = strtol(name, &endptr, 10); /* As a convenience to caller */ if (*endptr == '\0') /* allow a numeric string */ return g; grp = getgrnam(name); if (grp == NULL) return -1; return grp->gr_gid; }users_groups/ugid_functions.c
The setpwent(), getpwent(), and endpwent() functions are used to perform sequential scans of the records in the password file.
#include <pwd.h>
struct passwd *getpwent
(void);
Returns pointer on success, or NULL
on end of stream or error
voidsetpwent
(void); voidendpwent
(void);
The getpwent() function returns records from the password file one by one, returning NULL
when there are no more records (or an error occurs). On the first call, getpwent() automatically opens the password file. When we have finished with the file, we call endpwent() to close it.
We can walk through the entire password file printing login names and user IDs with the following code:
struct passwd *pwd; while ((pwd = getpwent()) != NULL) printf("%-8s %5ld\n", pwd->pw_name, (long) pwd->pw_uid); endpwent();
The endpwent() call is necessary so that any subsequent getpwent() call (perhaps in some other part of our program or in some library function that we call) will reopen the password file and start from the beginning. On the other hand, if we are part-way through the file, we can use the setpwent() function to restart from the beginning.
The getgrent(), setgrent(), and endgrent() functions perform analogous tasks for the group file. We omit the prototypes for these functions because they are similar to those of the password file functions described above; see the manual pages for details.
The following functions are used to retrieve individual records from the shadow password file and to scan all records in that file.
#include <shadow.h>
struct spwd *getspnam
(const char *name);
Returns pointer on success, or NULL
on not found or error
struct spwd *getspent
(void);
Returns pointer on success, or NULL
on end of stream or error
voidsetspent
(void); voidendspent
(void);
We won’t describe these functions in detail, since their operation is similar to the corresponding password file functions. (These functions aren’t specified in SUSv3, and aren’t present on all UNIX implementations.)
The getspnam() and getspent() functions return pointers to a structure of type spwd. This structure has the following form:
struct spwd { char *sp_namp; /* Login name (username) */ char *sp_pwdp; /* Encrypted password */ /* Remaining fields support "password aging", an optional feature that forces users to regularly change their passwords, so that even if an attacker manages to obtain a password, it will eventually cease to be usable. */ long sp_lstchg; /* Time of last password change (days since 1 Jan 1970) */ long sp_min; /* Min. number of days between password changes */ long sp_max; /* Max. number of days before change required */ long sp_warn; /* Number of days beforehand that user is warned of upcoming password expiration */ long sp_inact; /* Number of days after expiration that account is considered inactive and locked */ long sp_expire; /* Date when account expires (days since 1 Jan 1970) */ unsigned long sp_flag; /* Reserved for future use */ };
We demonstrate the use of getspnam() in Example 8-2.