9

This question already has an answer here:

In Ubuntu 14.04, the passwd executable is

-rwsr-xr-x 1 root root 47032 gen 27 01:50 /usr/bin/passwd

and the ping executable is

-rwsr-xr-x 1 root root 44168 mag  7  2014 /bin/ping

so (for both) the uid of the running process should result root, even when they are ran from a regular user. If I run passwd from user1, in fact, I obtain

$ ps -aux | grep passwd
root      4317  0.0  0.0  85940  2004 pts/0    S+   10:24   0:00 passwd

but if I run ping from user1 it is not the same:

$ ps -aux | grep ping
user1    4362  0.0  0.0   6500   632 pts/0    S+   10:29   0:00 ping 192.168.8.1

Why the uid of the process has been set to root in the first case and not in the second one?

marked as duplicate by JdeBP, Rui F Ribeiro, Stephen Rauch, cas, Anthon Jul 31 '17 at 8:36

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

5

The general sense of @rui-f-ribeiro remark is correct, but the details are not. Details matter. Ubuntu uses these packages:

The ping utility resets permissions in a function named limit_capabilities, shared by ping and ping6. The relevant chunk of code looks like this:

        if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
                perror("ping: prctl");
                exit(-1);
        }

        if (setuid(getuid()) < 0) {
                perror("setuid");
                exit(-1);
        }

        if (prctl(PR_SET_KEEPCAPS, 0) < 0) {
                perror("ping: prctl");
                exit(-1);
        }

        cap_free(cap_p);
        cap_free(cap_cur_p);
#endif
        uid = getuid();
        euid = geteuid();
#ifndef CAPABILITIES
        if (seteuid(uid)) {
                perror("ping: setuid");
                exit(-1);
        }
#endif

That is (read the source code), ping performs several privileged operations and drops privileges — but it can be built to behave in different ways according to one's preference.

Interestingly, the changelog notes:

iputils (3:20121221-2) unstable; urgency=low

  * Enable the CAP_NET_RAW capability and strip the setuid bit on ping and
    ping6 binaries if possible.

The story for passwd is similar, with different details. It is part of the shadow suite of tools, which may drop privileges in change_root:

    /* Drop privileges */
    if (   (setregid (getgid (), getgid ()) != 0)
        || (setreuid (getuid (), getuid ()) != 0)) {
            fprintf (stderr, _("%s: failed to drop privileges (%s)\n"),
                     Prog, strerror (errno));
            exit (EXIT_FAILURE);
    }

But it only does this in a special case:

/*
 * process_root_flag - chroot if given the --root option
 *
 * This shall be called before accessing the passwd, group, shadow,
 * gshadow, useradd's default, login.defs files (non exhaustive list)
 * or authenticating the caller.
 *
 * The audit, syslog, or locale files shall be open before
 */

In the usual case, it ensures that it has privileges and does not drop them (because there is nothing else to do which doesn't require the privilege):

    if (setuid (0) != 0) {
            (void) fputs (_("Cannot change ID to root.\n"), stderr);
            SYSLOG ((LOG_ERR, "can't setuid(0)"));
            closelog ();
            exit (E_NOPERM);
    }

Most utilities do not reset the setuid/setgid behavior, presuming that they are not installed with those permissions.

5

As @schily says, in the ping utilility (and others), the root permissions are dropped after they are no longer necessary. This is done for security reasons.

From ping.c - main() - the user root is dropped with the getuid and setuid call.

getuid() gets the current user, and root doing a setuid() will change the uid of the process.

/*
 * Pull this stuff up front so we can drop root if desired.
 */
if (!(proto = getprotobyname("icmp"))) {
    (void)fprintf(stderr, "ping: unknown protocol icmp.\n");
    exit(2);
}
if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
    if (errno==EPERM) {
        fprintf(stderr, "ping: ping must run as root\n");
    }
    else perror("ping: socket");
    exit(2);
}

#ifdef SAFE_TO_DROP_ROOT    
    setuid(getuid());       /* HERE RETURNING TO THE USER */
#endif
4

The uid of the second process has been reset already because there is no need to be root anymore after the sockets have been opened.

The passwd utility still needs root privileges when you checked.

If you like to verify this, you will need to check the source-code as the reset of the uid may be done too fast to give others a chance to verify the uid before the reset.

  • Right question, @heemayl. And here is another: who is devoted to reset the uid of ping? The kernel? – BowPark May 10 '16 at 9:03
  • 1
    The utility itself does the reset. – schily May 10 '16 at 9:36

Not the answer you're looking for? Browse other questions tagged or ask your own question.