💾 Archived View for thrig.me › blog › 2024 › 07 › 05 › rcs-surprise.gmi captured on 2024-08-25 at 01:08:21. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2024-07-09)

-=-=-=-=-=-=-

RCS surprise

RCS uses getlogin(2) to "lock" a checkout to a particular account,

    $ grep getlogin /usr/src/usr.bin/rcs/co.c
                                    if ((author = getlogin()) == NULL)
                                            err(1, "getlogin");
            if ((username = getlogin()) == NULL)
                    err(1, "getlogin");

which means a checked out file may end up with a conflicting username in it, if checked out by a regular user but then checked in by a process that runs as, say, root:

    $ touch foo
    $ ci -minitial -t-test foo
    foo,v  <--  foo
    initial revision: 1.1
    done
    $ co -l -q foo
    $ sed -n '/^lock/{n;p;q;}' foo,v
            jhqdoe:1.1; strict;
    $ ci -q foo
    $ doas co -l -q foo
    $ sed -n '/^lock/{n;p;q;}' foo,v
            jhqdoe:1.1; strict;
    $ echo 'ci foo 2>log' | doas at now
    ...
    $ cat log
    foo,v  <--  foo
    ci: foo,v: no lock set by root

The at(1) trick shows one method by which a root login can be obtained, as the getlogin value may persist where one might not expect it to.

    $ perl -E 'say getlogin'
    jhqdoe
    $ doas perl -E 'say getlogin'
    jhqdoe
    $ su -l root
    Password:
    # perl -E 'say getlogin'
    jhqdoe

Another trick, or kluge, is to rewrite the login to the account the cron jobs or whatever will run as. Note that there's a literal tab within the sed command, not a bunch of spaces before "root". (You did look at the file with a hex viewer to confirm what all the bytes are, right?)

    $ sed -i '/^locks/{n;s/^[^:]*/	root/;}' foo,v

This will need to be made more complicated if "^locks" can match somewhere else in the initial file, as in that case the above may mangle the file content:

    $ printf 'locks\nfoo\nlocks\nbar\n' |
      sed '/^locks/{n;s/[a-z]*/mangle/;}'
    locks
    mangle
    locks
    mangle
    $ printf 'locks\nfoo\nlocks\nbar\n' |
      perl -ne \
      'if(/^locks/ and !$d){print;$_=readline;s/[a-z]*/first/;$d=1};print'
    locks
    first
    locks
    bar

Or configuration management could install the right foo and foo,v files at the beginning, in which case the bootstrap would only need to happen when the starting files are put into the configuration management repository.