18

I'm trying to use setuid() and setgid() to set the respective id's of a program to drop privileges down from root, but to use them I need to know the uid and gid of the user I want to change to.

Is there a system call to do this? I don't want to hardcode it or parse from /etc/passwd .

Also I'd like to do this programmatically rather than using:

id -u USERNAME

Any help would be greatly appreciated

roalz
  • 2,699
  • 3
  • 25
  • 42
Evan
  • 635
  • 1
  • 8
  • 19
  • What's wrong with parsing /etc/passwd? – eduffy Jun 17 '09 at 20:10
  • 1
    I should have said, "I'd rather not parse /etc/passwd", since I suspected there was an easier way, and thanks to the answers, I've found it. – Evan Jun 17 '09 at 20:39
  • 11
    One thing wrong with parsing /etc/passwd is that the user names may not be stored there - they may be out on a directory server (LDAP, etc) somewhere. Another thing wrong is that it is already done for you, by getpwnam() et al. – Jonathan Leffler Jun 18 '09 at 00:34

5 Answers5

25

Have a look at the getpwnam() and getgrnam() functions.

jpalecek
  • 47,058
  • 7
  • 102
  • 144
4
#include <sys/types.h>
#include <pwd.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
    char *username = ...

    struct passwd *pwd = calloc(1, sizeof(struct passwd));
    if(pwd == NULL)
    {
        fprintf(stderr, "Failed to allocate struct passwd for getpwnam_r.\n");
        exit(1);
    }
    size_t buffer_len = sysconf(_SC_GETPW_R_SIZE_MAX) * sizeof(char);
    char *buffer = malloc(buffer_len);
    if(buffer == NULL)
    {
        fprintf(stderr, "Failed to allocate buffer for getpwnam_r.\n");
        exit(2);
    }
    getpwnam_r(username, pwd, buffer, buffer_len, &pwd);
    if(pwd == NULL)
    {
        fprintf(stderr, "getpwnam_r failed to find requested entry.\n");
        exit(3);
    }
    printf("uid: %d\n", pwd->pw_uid);
    printf("gid: %d\n", pwd->pw_gid);

    free(pwd);
    free(buffer);

    return 0;
}
Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539
  • You never free `pwd` or `buffer` - but why do you even bother to allocate `pwd` or `buffer` on the heap anyway – Siler Nov 07 '14 at 05:14
  • @Siler, I added the frees. I agree it can be done on the stack (the only caveat is to make sure sysconf(_SC_GETPW_R_SIZE_MAX) is not too big (it's unlikely to be on any realistic system). However, I'm not going to rewrite it for that. – Matthew Flaschen Nov 14 '14 at 06:39
  • This is great _when you know the username_, but is not a replacement for `getuid()`. – Jesse Chisholm Oct 19 '17 at 16:08
3

You want to use the getpw* family of system calls, generally in pwd.h. It's essentially a C-level interface to the information in /etc/passwd.

Tim Gilbert
  • 5,721
  • 5
  • 33
  • 28
3

You can use the following code snippets:

#include <pwd.h>
#include <grp.h>

gid_t Sandbox::getGroupIdByName(const char *name)
{
    struct group *grp = getgrnam(name); /* don't free, see getgrnam() for details */
    if(grp == NULL) {
        throw runtime_error(string("Failed to get groupId from groupname : ") + name);
    } 
    return grp->gr_gid;
}

uid_t Sandbox::getUserIdByName(const char *name)
{
    struct passwd *pwd = getpwnam(name); /* don't free, see getpwnam() for details */
    if(pwd == NULL) {
        throw runtime_error(string("Failed to get userId from username : ") + name);
    } 
    return pwd->pw_uid;
}

Ref: getpwnam() getgrnam()

Archit Jain
  • 2,154
  • 1
  • 18
  • 32
  • Details: should you use `nullptr` instead of NULL?; A good coding practice/style is to set the literal left of the variable: `nullptr == grp` in order to avoid issues if assigned instead of compared by mistake. – Adrian Maire Aug 15 '19 at 14:39
1

Look at getpwnam and struct passwd.

Joe
  • 41,484
  • 20
  • 104
  • 125