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


which groups its process (or rather, it's process's user) is part of. For example, consider a file manager that allows us to change the group owning a file. It will present us with a menu containing all groups which we belong to. For this, the getgroups() system call may be used. This system call accepts two parameters: the maximal number of groups IDs we want to receive, and an array into which the GIDs will be stored by the system call. getgroups() returns the number of GIDs actually stored in the array. A special case is if we supply '0' in the first parameter. In this case, the array is unmodified, and the return value is the total number of groups associated with the process. Here is how we can receive all the groups in a single call:




#include <unistd.h>     /* getgroups(), etc. */

/* this array will be used to store the GIDs. */
gid_t** group_ids;
int num_groups;

/* first, check how many groups our process actually has. */
num_groups = getgroups(0, group_ids);
if (num_groups < 0) {
    perror("getgroups");
    exit(1);
}
if (num_groups > 0) {
    int i;

    group_ids = (gid_t**)malloc(num_groups * sizeof(gid_t));
    getgroups(num_groups, group_ids);
    printf("GIDs of our process:\n");
    for (i=0; i<num_groups; i++)
 printf("%d,", group_ids[i]);
    printf("\n");
}




Note that we called getgroups() twice - once to find the number of groups (so we could allocate a large enough array), and once to actually get the GIDs. We could then translate the GIDs into group names (using the getgrgid() function we saw earlier), and present that to the user (in our file manager menu's example).




The Sick Case Of Set-User-ID (suid) Programs
If you remember, we mentioned earlier that the "/etc/passwd" file is only writable for "root". Yet, there is a program named "passwd" that allows other users to change the contents of certain fields in this file. You might wonder how this is done. The idea is rather simple. If you check out the permissions of the "passwd" program, you will see something like this (using 'ls -l /usr/bin/passwd'):


-r-sr-xr-x   1 root     bin         15796 Apr 23  1997 /usr/bin/passwd

As you can see, instead of an 'x' for the owner, there is a lower-case 's'. This indicates that this program has the "set user-ID" (or SUID for short) bit - set. When the operating system launches a program that has the SUID bit set, it will give the process running it the identity of the user that owns the file - in this case, "root". This means that this process has the same access permissions as "root" has, rather then as the user that started this process. This way, when user "john" runs the "passwd" program, he may alter the "/etc/passwd" file, that he could not alter directly. It is up to the program to make sure that he only may change fields of his own account, and indeed, many security holes on Unix systems are due to bugs in SUID programs, that allow users that run them to perform operations they shouldn't be able to.

The method by which the operating system handles SUID programs, is by giving each process two identities: the real UID, and the effective UID. The real UID is the UID associated with the process that launched our process. The effective UID is the same as the real UID for normal programs, or it is the owner of the executable file, for SUID programs. These changes may also be done using the setuid() and seteuid() functions, under various restrictions. These system calls are used (for example) by the "login" program (that runs as user "root" initially), to spawn a shell whose real and effective UID are those of the user that logs in.

To find the real UID of a running process, we may use the getuid() system call. To find the effective UID - we use the geteuid() system call. If you remember, when we demonstrated finding the user name of our process earlier, we used the getuid() system call. However, if we wanted to find what actions the process will be actually able to do, we should have checked the effective UID of the process - this is because we need to handle the case of SUID programs - where the effective UID and the real UID are different.

Note: on Unices derived from the BSD system, there is a third type of UID, called the stored UID, that further complicates the rules of setting a process's real and effective UID.




Who Is Currently Logged On?
One thing that might interest our program is - who is currently logged on to our system? For example, the finger program prints that information our to users. Messaging programs (write, talk) use this information to find if the person we try to send a message to is logged on the system or not.

For this purpose, the "login" program writes down information in a file named "utmp" (often residing in the "/var/log", "/var/adm" or "/var/run" directory). This file is a binary file, and each entry is a record of a fixed size. There is no standard API used to read the contents of this file, but there is a data structure, defined in the "utmp.h" include file, that gives us the exact format of a record. Here is how it is defined on a Linux system:

struct utmp {
    short ut_type;              /* type of login */
    pid_t ut_pid;               /* pid of process */
    char ut_line[UT_LINESIZE];  /* device name of tty - "/dev/" */
    char ut_id[2];              /* init id or abbrev. ttyname */
    time_t ut_time;             /* login time */
    char ut_user[UT_NAMESIZE];  /* user name */
    char ut_host[UT_HOSTSIZE];  /* host name for remote login */
    long ut_addr;               /* IP addr of remote host */
};


This structure may be defined differently on different operating systems, but it will usually contain at least the user name and the time of the login. For our purpose, the login name is all that matters. Here is a sample program that lists the names of the currently logged in users:

#include <stdio.h>     /* printf(), etc.    */
#include <utmp.h>      /* struct utmp, etc. */
#include <string.h>    /* strncpy(), etc.   */
#include <unistd.h>    /* open(), etc.      */
#include <fcntl.h>     /* O_RDONLY, etc.    */

/* full path for the UTMP file on our sample system. */
#define UTMP_PATH "/var/run/utmp"

/* buffer to read one utmp record. */
struct utmp utmp_entry;
/* buffer to store a user name. */
char user_name[UT_NAMESIZE+1];
/* file descriptor for the "utmp" file. */
int fd;

/* open the utmp file for reading. */
fd = open(UTMP_PATH, O_RDONLY);
if (fd < 0) {
    perror("open");
    exit(1);
}

printf("Currently logged-in users:\n");
/* start scanning the file, entry by entry. */
while (1) {
    int rc = read(fd, &utmp_entry, sizeof(utmp_entry));
    if (rc < 0) {
        perror("read");
        exit(1);
    }
    if (rc == 0) /* end of file - exit the reading loop. */
        break;

    /* This is Linux specific - only records whose ut_type field is    */
    /* USER_PROCESS, represent logged in users. the rest are temporary */
    /* records of various types.                                       */
    if (utmp_entry.ut_type != USER_PROCESS)
        continue;

    /* copy the user name field to our user name variable. */
    strncpy(user_name, utmp_entry.ut_name, UT_NAMESIZE);
    /* make sure this string is terminated with a null character. */
    user_name[UT_NAMESIZE] = '\0';

    printf("'%s',", user_name);
}

printf("\n");
close(fd);





The source code of this program is also available in the show-logged-users.c file.

A few notes are in place:
The code above is not portable, as not all

Page : << Previous 4  Next >>