Topic : Accessing User Information
Author : LUPG
Page : << Previous 3  Next >>
Go to page :

running a password cracker that will in turn try to guess passwords for all accounts. On most systems, and with most users, this will allow them to guess passwords of a few accounts at least.

In order to reduce this risk, shadow passwords were invented. On a system that uses shadow passwords, the passwords of the users are kept in a different file, that is only readable to its owner - root. the password entry in the "/etc/passwd" file (for each user) will contain a special mark-up character, defining that the actual password is kept in the shadow file. This mark-up is done differently on different operating systems, and the shadow file's path is also different for different systems. Thus, although shadow passwords exist for at least a decade, there is no standard implementation for them.

When reading a user account's info (using getpwnam() for example), the password field in the returned structure will contain the place-holder string, rather then the encrypted password. This makes it impossible to write simple programs that authenticate users using their account's password. The solution to this problem is usually a specialized (non-standard) function supplied by the operating system, to perform such authentication. Such a password will normally accept a user name and an encrypted password, and will return '1' if this encrypted password matches that of the given user, or '0' if it does not. This still allows people to write password crackers, but then they must be ran on the same system whose accounts they are trying to break into, making it much easier to spot this activity and to block it (e.g. by disabling an account if too many authentication attempts are made against it, in a given period of time).

Association Of Extra Groups For Users
On a Unix system, it is possible to associate a user with more then one group. This is done using the "/etc/group" file, together with a set of APIs.

Structure Of The /etc/group File
The "/etc/group" file is a text file that contains a set of lines, each defining membership of a list of users in a given group. Each group may be defined by one or more lines, and the users of this group include all the users defined in all these lines, as well as the users having this group's GID (Group ID) in their "/etc/passwd" entries. Here is an example of a valid "/etc/group" line:


This example shows some information about the "bin" group. This line is composed of a set of fields, separated by a colon (':') character (like in the "/etc/passwd" file). The fields serve the following purposes:
Group Name
The name of the group this line defines.
The password for this group. Normally empty, and implying that no password is required for the group.
Group ID.
The numeric ID of the given group.
Users List
A list of user names that belong to this group. The names are separating using comma (',') characters.
Thus, the above line states that the "bin" group has a GID of '1', and has users "root", "bin" and "daemon" as members. Again, remember that there may be several lines for the group "bin" in this file, and they should all have the same GID. The order of such lines is not important.

Finally, note that much like the "/etc/passwd" file, the "/etc/group" file is readable by anyone, and writable only by its owner - root. However, unlike for the "/etc/passwd" file, there are no standard programs that allow changing the contents of the "/etc/group" file.

Listing The Users In A Given Group
The first API we will deal with is used to get the list of users belonging to a given group. This is done using the getgrnam() or the getgrgid() functions.

The getgrnam() function gets a group name as its parameter, and returns a pointer to a struct group containing information about the given group. The function will return NULL if the group does not exist on the system. The group structure that is returned is defined as follows:

struct group {
    char    *gr_name;    /* group name */
    char    *gr_passwd;  /* group password */
    gid_t   gr_gid;      /* group id */
    char    **gr_mem;    /* group members */

Note that the gr_mem field is made of a list of user names, terminated by a NULL pointer. This group will contain all members of the group - no matter if they are defined on several separate lines. However, users associated with the group only via the GID field in their "/etc/passwd" entries (and not via proper lines in the "/etc/group" file) would NOT be included in this list. This makes this function somewhat broken.
Here is an example of a code that prints out the list of members in the "strange" group:

#include <grp.h> /* defines 'struct group', and getgrnam(). */
#include <sys/types.h> /* defines 'gid_t', etc.              */

/* get the information about the "strange" group. */
struct group* group_info = getgrnam("strange");
/* make sure this group actually exists. */
if (!group_info) {
    printf("group 'strange' does not exist.\n");
else {
    char** p_member;

    printf("Here are the members of group 'strange':\n");
    for (p_member = group_info->gr_mem; *p_member; p_member++)
        printf("  %s\n", *p_member);

The getgrgid() function works the same as getgrnam(), except that its parameter is the numeric GID of the desired group. hopefully you would be able to write your own example of using this function.

One last note about the getgrnam() and getgrgid() functions is that just like the getpwnam() function, they return pointers to statically allocated structures, and thus erase the results of the previous call on each consecutive call.

Enumerating (almost) All Groups
Similarly to the API used to enumerate user accounts, there are APIs used to enumerate "/etc/group" file entries. There are getgrent(), setgrent() and endgrent(). they work in exactly the same way as their counter APIs for the "/etc/group" file. Here is a quick example:

/* start the scan of the "/etc/group" file. */
struct group* group_info = getgrent();

/* loop over all group entries, until a NULL pointer is returned. */
printf("Here is a list of all groups found in '/etc/group':\n");
for ( ; group_info; group_info = getgrent() ) {
    printf(" %s\n", group_info->gr_name);

We won't demonstrate the usage of setgrent() and endgrent() - that should be obvious by now. If it is not, re-read the user accounts enumeration section.

Groups In /etc/group Vs. Groups In /etc/passwd
As it was mentioned before, groups may be defined in two places: by using the GID field of a user's entry in the "/etc/passwd" file, and by an entry in the "/etc/group" file. A group specified in the "/etc/group" file always has a group name, while a group specified in the "/etc/passwd" file does not have a name (unless there is a group with the same GID defined in the "/etc/group" file).

Another difference (already mentioned before) is that the getgr*() functions will show members specified in the "/etc/group" file, but will not show members specified via the "/etc/group" file.

Finally, we will refer the user to the groups command. This command will print the names of all groups the currently logged on user belongs to, or all groups a given user belongs to. This list of group will contain all relevant entries, whether found in the "/etc/group" file, or in the "/etc/passwd" file. Read the manual page for the "groups" command for more information.

Determining User Info At Run-Time
One of the things programs often need to know is which user they perform as, what groups they belong to, etc. For this, we should note that each process running on a Unix system, has a specific UID and a specific set of GIDs. These are usually the UID and GIDs of the user running the process, but there are exceptions.

Which User Is My Process Running As?
Determining the UID of a process is done using the getuid() system call, and a set of its cousins that will be discussed when we explain about Set UID programs below. This system call gets no parameters, and returns the effective UID of the process. Here is an example that prints out the name of the user executing this process:

/* Find the "/etc/passwd" entry of the UID of our process. */
struct passwd* user_info = getpwuid(getuid());

/* Print out the results. */
if (!user_info)
    printf("Error, cannot determine who i am.\n");
    printf("A-ha! I am %s\n", user_info->pw_name);

As the getuid() system call cannot fail, there is no need (and no way) to check its success.

Which Groups Is My Process Associated With?
Sometimes our program might want to know

Page : << Previous 3  Next >>