password (& host?) file lookups, daemons and stray descriptors

From: Mr. Uh Clem (uhclem_at_DutchElmSt.invalid)
Date: 06/24/05

  • Next message: Sandy: "Why I could not catch the signals on HP_UX?"
    Date: Thu, 23 Jun 2005 18:42:22 -0400
    
    

    I've run into a strange problem on an HP11i system involving
    a daemon program which

    * calls getpwuid() as part of a security check during
       initialization

    * does the normal daemonization things:
        forks, setsid, etc.,
        close all descriptors,
        redirect 0-2 to /dev/null.

    * opens a lockfile and does a lockf on it.

    * subsequently calls getpwnam() to obtain a uid.

    On this system, the getpwnam() call causes the
    lock to be lost on the lockfile. (We don't seem
    to have this problem on our various other *ix
    flavors, although that could be a matter of
    system configuration: nsswitch.conf, etc.)

    Debugging shows that getpwuid leaves a descriptor
    open after it is called. From truss, it looks as
    if it creates a socket and leaves it open on fd 3.
    It is not hard to believe that Bad Things Happen(tm)
    when the daemonizing closes all child fds and the
    lockfile winds up with file descriptor 3. Truss
    seems to show the getpwnam() call atempting a sendto()
    on fd 3, failing, then doing it's thing on 4 and
    closing 4.

    Well, I thought I could deal with this by calling
    endpwent() after the getwpuid(), but the extra
    descriptor does not go away and the problem persists.
    This is not the post I thought it would be...

    It took a while to figure out how to make it fail
    from a simple program, but I finally did:

    #include <sys/types.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <errno.h>
    #include <pwd.h>
    #include <errno.h>

    int check_fds(char *where);

    main()
    {
        struct passwd *pw;
        int status, fd;

        check_fds("main");

        status = getuid();
        printf("getuid() is %d\n", status);
        if(status == -1)
           exit(11);
        pw = getpwuid(status);
        if (pw == NULL)
           exit(12);
        printf("user name is %s\n", pw->pw_name);

        check_fds("after getpwuid");
        endpwent();
        check_fds("after endpwent");

        /* simulate daemonize */
        close(3);
        close(4);

        fd = open("/tmp/mylock", O_RDWR);
        if (fd == -1)
           {
           perror("open");
           exit(1);
           }
        printf("locfile open on fd %d\n", fd);
        status = lockf(fd, F_TLOCK, 0);
        if (status == -1)
           exit(2);
        printf("locked\n");
        getc(stdin);

        pw = getpwnam("bin"); [ this call seems to lose the lock ]
        if (pw == NULL)
           exit(3);
        check_fds("after getpwnam");
        printf("got uid %d\n", pw->pw_uid);
        getc(stdin);

        printf("exiting\n");
        exit(0);
    }

    int check_fds(char *where)
    {
        int status, fd;

        for (fd = 0; fd < 10; fd++)
           {
           status = dup(fd);
           if (status != -1)
              {
              printf("%d ", fd);
              close(status);
              }
           }
        printf("- %s\n", where);
        return(0);
    }

    # gcc test2.c
    # ./a.out
    0 1 2 - main
    getuid() is 0
    user name is root
    0 1 2 3 - after getpwuid
    0 1 2 3 - after endpwent
    locfile open on fd 3
    locked
    [the file is locked at this point]

    0 1 2 3 - after getpwnam
    got uid 2
    [the file is unlocked at this point]

    exiting
    #
    [instead of here, when the program exits]

    If I run under truss, an extra fd
    seems to get injected:

    execve("./a.out", 0x7b0404a0, 0x7b0404a8) = 0
    [32-bit]
    open("/usr/lib/dld.sl", O_RDONLY, 02204) = 4
    read(4, "02\v010e0512@ \0\0\0\0\0\0\0\0\0".., 128) = 128
    lseek(4, 128, SEEK_SET) = 128
    read(4, "10\0\004\0\0\0( \002e884\0\0\0\0".., 48) = 48
    mmap(NULL, 190596, PROT_READ|PROT_EXEC,
    MAP_SHARED|MAP_SHLIB|MAP_STATICPREDICTION, 4, 0x9000) = 0xc0010000
    mmap(NULL, 16104, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_SHLIB,
    4, 0x38000) = 0x7b03c000
    close(4) = 0
    mmap(NULL, 8192, PROT_READ|PROT_WRITE|PROT_EXEC,
    MAP_PRIVATE|MAP_ANONYMOUS, -1, NULL) = 0x7b03a000
    sysconf(_SC_CPU_VERSION) = 532
    open("/opt/graphics/OpenGL/lib/libogltls.sl", O_RDONLY, 0) = 4
    fstat(4, 0x7b042f98) = 0
    read(4, "0210010e0512@ \0\0\0\0\0\0\0\0\0".., 128) = 128
    lseek(4, 128, SEEK_SET) = 128
    read(4, "10\0\004\0\0\0( \0\013\ \0\010\0".., 48) = 48
    read(4, "80\0\001\0\0\0 9 2 4 5 ", 12) = 12
    lseek(4, 8192, SEEK_SET) = 8192
    read(4, "058cy 10\0\0\0D \0\002$ \0\0\001".., 112) = 112
    mmap(NULL, 8192, PROT_READ|PROT_EXEC, MAP_SHARED|MAP_SHLIB, 4, 0x2000) =
    0xc0005000
    mmap(NULL, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_SHLIB,
    4, 0x4000) = 0x7b039000
    close(4) = 0
    open("/usr/lib/libc.2", O_RDONLY, 0) = 4
    fstat(4, 0x7b043158) = 0
    read(4, "0214010e0512@ \0\0\0\0\0\0\0\0\0".., 128) = 128
    lseek(4, 128, SEEK_SET) = 128
    read(4, "10\0\004\0\0\0( \013y c4\0\010\0".., 48) = 48
    read(4, "80\0\0\v\0\0\004\0\0\0\0", 12) = 12
    lseek(4, 430080, SEEK_SET) = 430080
    read(4, "058cy 10\0\0\ad \0\0K L \0\0\002".., 112) = 112
    mmap(NULL, 1277952, PROT_READ|PROT_EXEC, MAP_SHARED|MAP_SHLIB, 4,
    0x69000) = 0xc0100000
    mmap(NULL, 45056, PROT_READ|PROT_WRITE|PROT_EXEC,
    MAP_PRIVATE|MAP_ANONYMOUS|MAP_SHLIB, -1, NULL) = 0x7b02e000
    mmap(0x7b026000, 32768, PROT_READ|PROT_WRITE|PROT_EXEC,
    MAP_PRIVATE|MAP_FIXED|MAP_SHLIB, 4, 0x1a1000) = 0x7b026000
    mmap(NULL, 16384, PROT_READ|PROT_WRITE|PROT_EXEC,
    MAP_PRIVATE|MAP_ANONYMOUS, -1, NULL) = 0x7b022000
    close(4) = 0
    open("/usr/lib/libdld.2", O_RDONLY, 0) = 4
    fstat(4, 0x7b043318) = 0
    read(4, "02\v010e0512@ \0\0\0\0\0\0\0\0\0".., 128) = 128
    lseek(4, 128, SEEK_SET) = 128
    read(4, "10\0\004\0\0\0( \0\0) \ \0\010\0".., 48) = 48
    read(4, "80\0\0\v\0\0\004\0\0\0\0", 12) = 12
    lseek(4, 8192, SEEK_SET) = 8192
    read(4, "058cy 10\0\0\0\f\0\00214\0\0\001".., 112) = 112
    mmap(NULL, 12288, PROT_READ|PROT_EXEC, MAP_SHARED|MAP_SHLIB, 4, 0x2000)
    = 0xc0007000
    mmap(NULL, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_SHLIB,
    4, 0x5000) = 0x7b021000
    close(4) = 0
    open("/usr/lib/libc.2", O_RDONLY, 0) = 4
    fstat(4, 0x7b043498) = 0
    close(4) = 0
    mmap(NULL, 16384, PROT_READ|PROT_WRITE|PROT_EXEC,
    MAP_PRIVATE|MAP_ANONYMOUS, -1, NULL) = 0x7b01d000
    mmap(NULL, 3368, PROT_READ|PROT_WRITE|PROT_EXEC,
    MAP_PRIVATE|MAP_ANONYMOUS, -1, NULL) = 0x7b01c000
    sigsetreturn(0x7b023f36, 0x6211988, 1392) = 0

    looks like we start here...

    dup(0) = 4
    sysconf(_SC_CPU_VERSION) = 532
    brk(0x40001140) = 0
    brk(0x40003134) = 0
    brk(0x40006000) = 0
    ioctl(1, TCGETA, 0x7b040958) = 0
    close(4) = 0
    dup(1) = 4
    close(4) = 0
    dup(2) = 4
    close(4) = 0
    dup(3) = 4
    close(4) = 0
    dup(4) ERR#9 EBADF
    dup(5) ERR#9 EBADF
    dup(6) ERR#9 EBADF
    dup(7) ERR#9 EBADF
    dup(8) ERR#9 EBADF
    dup(9) ERR#9 EBADF
    write(1, "0 1 2 3 - m a i n \n", 15) = 15
    getuid() = 0 (0)
    write(1, "g e t u i d ( ) i s 0 \n", 14) = 14

    If I close(3) at the start of the program to get rid of the
    extraneous fd truss seems to give, the following winds up on
    fd 3.

    open("/var/spool/pwgr/status", O_RDONLY, 0) = 4
    mmap(NULL, 532, PROT_READ, MAP_SHARED|MAP_VARIABLE|MAP_FILE|MAP_ADDR32,
    4, NULL) = 0xc0057000
    close(4) = 0
    socket(AF_UNIX, SOCK_DGRAM, 0) = 4
    getpid() = 19401
    (19400)
    unlink("/var/spool/sockets/pwgr/client19401") ERR#2
    ENOENT
    bind(4, 0x7b02f580, 38) = 0
    fcntl(4, F_SETFD, 1) = 0
    time(NULL) =
    1119565603
    poll(0x7b041798, 1, 0) = 1
    sendto(4, "\0\0\00 \0\0\001\0\0\0\0\0\0\001".., 48, 0, NULL, NULL) = 48
    poll(0x7b041798, 1, 1000) = 1
    recv(4, "\0\0\03 \0\0\0\0\0\0\0\0\0\0\0# ".., 2064, 0) = 51
    write(1, "u s e r n a m e i s r o o ".., 18) = 18
    dup(0) = 5
    close(5) = 0
    dup(1) = 5
    close(5) = 0
    dup(2) = 5
    close(5) = 0
    dup(3) = 5
    close(5) = 0
    dup(4) = 5
    close(5) = 0
    dup(5) ERR#9 EBADF
    dup(6) ERR#9 EBADF
    dup(7) ERR#9 EBADF
    dup(8) ERR#9 EBADF
    dup(9) ERR#9 EBADF
    write(1, "0 1 2 3 4 - a f t e ".., 27) = 27
    dup(0) = 5
    close(5) = 0
    dup(1) = 5
    close(5) = 0
    dup(2) = 5
    close(5) = 0
    dup(3) = 5
    close(5) = 0
    dup(4) = 5
    close(5) = 0
    dup(5) ERR#9 EBADF
    dup(6) ERR#9 EBADF
    dup(7) ERR#9 EBADF
    dup(8) ERR#9 EBADF
    dup(9) ERR#9 EBADF
    write(1, "0 1 2 3 4 - a f t e ".., 27) = 27
    close(3) = 0
    close(4) = 0
    open("/tmp/mylock", O_RDWR, 0160100) = 3
    write(1, "l o c f i l e o p e n o n ".., 21) = 21
    lockf(3, 0x2, 0) = 0
    write(1, "l o c k e d \n", 7) = 7
    brk(0x40008000) = 0
    ioctl(0, TCGETA, 0x7b040718) = 0
    read(0, 0x400059a0, 8192) [sleeping]
    read(0, "\n", 8192) = 1
    getpid() = 19401
    (19400)
    poll(0x7b041798, 1, 0) = 1

    When I close(3) at the start of the program to get rid of the
    fd truss gives, this sendto is performed against the lockfile
    and results in ENOTSOCK. In this case, where we don't even
    touch 3, the lock STILL gets lost! I don't see how..

    sendto(4, "\0\0\00 \0\0\001\0\0\001\0\0\002".., 48, 0, NULL, NULL) ERR#9
    EBADF
    open("/etc/nsswitch.conf", O_RDONLY, 0666) = 4
    brk(0x4000a000) = 0
    ioctl(4, TCGETA, 0x7b041618) ERR#25
    ENOTTY
    read(4, "# \n# / e t c / n s s w i t c ".., 8192) = 376
    brk(0x4000b000) = 0
    read(4, 0x40007e58, 8192) = 0
    close(4) = 0
    open("/usr/lib/libnss_files.1", O_RDONLY, 0) = 4
    fstat(4, 0x7b041818) = 0
    read(4, "0210010e0512@ \0\0\0\0\0\0\0\0\0".., 128) = 128
    lseek(4, 128, SEEK_SET) = 128
    read(4, "10\0\004\0\0\0( \0\0k T \0\010\0".., 48) = 48
    read(4, "80\0\0\v\0\0\004\0\0\0\0", 12) = 12
    lseek(4, 12288, SEEK_SET) = 12288
    read(4, "058cy 10\0\0\0D \0\004f0\0\0\002".., 112) = 112
    mmap(NULL, 28672, PROT_READ|PROT_EXEC, MAP_SHARED|MAP_SHLIB, 4, 0x3000)
    = 0xc0058000
    mmap(NULL, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_SHLIB,
    4, 0xa000) = 0x7b01b000
    close(4) = 0
    open("/usr/lib/libdld.2", O_RDONLY, 0) = 4
    fstat(4, 0x7b0419d8) = 0
    close(4) = 0
    open("/etc/passwd", O_RDONLY, 0666) = 4
    brk(0x4000d000) = 0
    ioctl(4, TCGETA, 0x7b041418) ERR#25
    ENOTTY
    read(4, "r o o t : R e 9 b n r j Y 8 i a ".., 8192) = 591
    lseek(4, 4294966794, SEEK_CUR) = 89
    close(4) = 0
    dup(0) = 4
    close(4) = 0
    dup(1) = 4
    close(4) = 0
    dup(2) = 4
    close(4) = 0
    dup(3) = 4
    close(4) = 0
    dup(4) ERR#9 EBADF
    dup(5) ERR#9 EBADF
    dup(6) ERR#9 EBADF
    dup(7) ERR#9 EBADF
    dup(8) ERR#9 EBADF
    dup(9) ERR#9 EBADF
    write(1, "0 1 2 3 - a f t e r ".., 25) = 25
    write(1, "g o t u i d 2 \n", 10) = 10
    read(0, 0x400059a0, 8192) [sleeping]
    read(0, "\n", 8192) = 1
    write(1, "e x i t i n g \n", 8) = 8
    getpid() = 19401
    (19400)
    unlink("/var/spool/sockets/pwgr/client19401") = 0
    exit(0)
    WIFEXITED(0)

    What the Fudd is going on ?? I'm stumped. I'd love
    to find out I'm doing Something Stupid(tm).

    -- 
    Clem
    "If you push something hard enough, it will fall over."
                  - Fudd's first law of opposition
    

  • Next message: Sandy: "Why I could not catch the signals on HP_UX?"

    Relevant Pages