Commit 48e37f3c authored by jan.koester's avatar jan.koester
Browse files

tokenizer end |

parent 4cbebce8
Loading
Loading
Loading
Loading
+17 −33
Changes for commands/auth/authutil.h: 17 added lines, 33 removed lines.
Original line number Diff line number Diff line
@@ -32,7 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// difference.
//
// Goes through the real libc account database (getpwnam()/getpwuid(),
// backed by /etc/user.yaml; getspnam()/shadow_update_hash(), backed by
// backed by /etc/user.yaml; getspnam()/shadow_set_password(), backed by
// /etc/shadow.yaml) instead of parsing /etc/passwd and /etc/shadow itself --
// one real account database for every consumer (whoami, id, ls -l's owner
// names, ...), not a second, independently-parsed copy of it just for these
@@ -105,30 +105,6 @@ namespace fresh { namespace auth {
        return result && hash==result;
    }

    // $6$ (SHA-512) salt: 16 chars from crypt's own base64-like alphabet,
    // read from /dev/urandom. Falls back to a fixed (still functional, just
    // not cryptographically random) salt if /dev/urandom can't be opened,
    // which should only ever happen if the caller isn't actually on Linux.
    inline std::string makeSalt(){
        static const char alphabet[]=
            "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
        unsigned char raw[16];
        std::FILE *f=std::fopen("/dev/urandom","rb");
        if(f){ if(std::fread(raw,1,sizeof(raw),f)!=sizeof(raw)) { /* best-effort */ } std::fclose(f); }
        else std::memset(raw,0x5a,sizeof(raw));

        std::string salt="$6$";
        for(unsigned char b: raw) salt+=alphabet[b % 64];
        salt+="$";
        return salt;
    }

    inline std::string hashPassword(const std::string &plaintext){
        std::string salt=makeSalt();
        char *result=crypt(plaintext.c_str(),salt.c_str());
        return result ? std::string(result) : std::string();
    }

    // Prompts on stdout, reads one line from stdin with terminal echo
    // disabled (restored afterward regardless of how reading ends).
    inline std::string readPassword(const std::string &prompt){
@@ -153,14 +129,22 @@ namespace fresh { namespace auth {
        return pass;
    }

    // Real, atomic (temp file + rename, done inside shadow_update_hash()
    // itself) update of one user's /etc/shadow.yaml passwd field. Caller
    // must already have permission to write /etc/shadow.yaml (this system
    // has no setuid bit machinery -- passwd is expected to run as root
    // already, e.g. from an install/provisioning step or a root shell, not
    // as an unprivileged self-service tool).
    inline bool rewriteShadowHash(const std::string &username,const std::string &newHash){
        return shadow_update_hash(username.c_str(),newHash.c_str())!=0;
    // Sets a user's password in /etc/shadow.yaml through the libc
    // (shadow_set_password(): atomic temp file + rename, under lckpwdf()).
    // A user with SMB access keeps it -- the NT hash next to the password
    // follows the new one (see <shadow.h>). Caller must already have
    // permission to write /etc/shadow.yaml (this system has no setuid bit
    // machinery -- passwd is expected to run as root already, e.g. from an
    // install/provisioning step or a root shell, not as an unprivileged
    // self-service tool).
    inline bool setPassword(const std::string &username,const std::string &password){
        if(lckpwdf()!=0)
            return false;
        bool ok=shadow_set_password(username.c_str(),password.c_str(),0)==0;
        int e=errno;
        ulckpwdf();
        errno=e;
        return ok;
    }

}}
+4 −10
Changes for commands/auth/passwd.cpp: 4 added lines, 10 removed lines.
Original line number Diff line number Diff line
@@ -39,9 +39,9 @@ namespace fresh { namespace cmd {

    // "passwd [USERNAME]" (default: the caller's own account). Non-root
    // callers may only change their own password, and must confirm the
    // current one first; root can change anyone's without that check. New
    // password hashed with a fresh random $6$ (SHA-512) salt and written
    // back into /etc/shadow via a temp-file rename (see authutil.h).
    // current one first; root can change anyone's without that check. The
    // libc hashes the new password ($6$, SHA-512, fresh salt) and keeps a
    // user's SMB access on it (see authutil.h, <shadow.h>).
    class PasswdCommand : public ShellCommand {
    public:
        PasswdCommand():ShellCommand("passwd"){}
@@ -94,13 +94,7 @@ namespace fresh { namespace cmd {
                return 1;
            }

            std::string newHash=auth::hashPassword(p1);
            if(newHash.empty()){
                std::cerr << "passwd: password hashing failed" << std::endl;
                return 1;
            }

            if(!auth::rewriteShadowHash(target,newHash)){
            if(!auth::setPassword(target,p1)){
                std::cerr << "passwd: failed to update /etc/shadow: " << strerror(errno) << std::endl;
                return 1;
            }
+24 −12
Changes for commands/network/ping.cpp: 24 added lines, 12 removed lines.
Original line number Diff line number Diff line
@@ -30,12 +30,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Tries the unprivileged Linux "ping socket" (SOCK_DGRAM+IPPROTO_ICMP,
// gated by net.ipv4.ping_group_range - the same mechanism /bin/ping itself
// uses to work without setuid/CAP_NET_RAW) first, and falls back to a raw
// ICMP socket - which does need CAP_NET_RAW - only if that fails. Matching
// is done by comparing the reply's source address against the target
// address rather than trying to track the kernel's identifier-rewriting
// rules across both socket types, since with a single target and a single
// sequential request-then-wait loop that is sufficient and avoids relying
// on ping-socket kernel internals that aren't part of any stable contract.
// ICMP socket - which does need CAP_NET_RAW - only if that fails. A reply
// must come from the target and carry the sequence number of the request
// waited for (so a late reply isn't counted for the next one); on a raw
// socket, which sees every ICMP packet on the host, its identifier must be
// ours as well - a ping socket's identifier is chosen by the kernel, which
// only delivers that socket's replies anyway.

#include <iostream>
#include <iomanip>
@@ -48,6 +48,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <cerrno>

#include <unistd.h>
#include <sys/stat.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/select.h>
@@ -123,6 +124,9 @@ namespace fresh { namespace cmd {
            int gaiErr=getaddrinfo(host.c_str(),nullptr,&hints,&res);
            if(gaiErr!=0){
                std::cerr << "ping: " << host << ": " << gai_strerror(gaiErr) << std::endl;
                struct stat st;
                if(gaiErr!=EAI_SYSTEM && stat("/etc/resolv.yaml",&st)!=0)
                    std::cerr << "ping: no nameserver configured (/etc/resolv.yaml is missing)" << std::endl;
                return 1;
            }
            struct sockaddr_in target;
@@ -162,8 +166,8 @@ namespace fresh { namespace cmd {
                struct icmphdr *icmp=(struct icmphdr*)packet.data();
                icmp->type=ICMP_ECHO;
                icmp->code=0;
                icmp->un.echo.id=pid;
                icmp->un.echo.sequence=seq;
                icmp->un.echo.id=htons((unsigned short)pid);
                icmp->un.echo.sequence=htons((unsigned short)seq);
                for(int i=0;i<payloadSize;++i)
                    packet[sizeof(struct icmphdr)+i]=(unsigned char)i;
                icmp->checksum=0;
@@ -197,13 +201,17 @@ namespace fresh { namespace cmd {
                    struct sockaddr_in from;
                    socklen_t fromLen=sizeof(from);
                    ssize_t n=recvfrom(sock,rbuf,sizeof(rbuf),0,(struct sockaddr*)&from,&fromLen);
                    if(n<0)
                        break;
                    if(from.sin_addr.s_addr!=target.sin_addr.s_addr){
                    {
                        struct timeval now; gettimeofday(&now,nullptr);
                        remaining=timeoutSec-timevalDiffMs(sentTime,now)/1000.0;
                    }
                    if(n<0){
                        if(errno==EINTR)
                            continue;
                        break;
                    }
                    if(from.sin_addr.s_addr!=target.sin_addr.s_addr)
                        continue;

                    const unsigned char *icmpStart=rbuf;
                    ssize_t icmpLen=n;
@@ -220,6 +228,10 @@ namespace fresh { namespace cmd {
                    struct icmphdr *reply=(struct icmphdr*)icmpStart;
                    if(reply->type!=ICMP_ECHOREPLY)
                        continue;
                    if(ntohs(reply->un.echo.sequence)!=(unsigned short)seq)
                        continue;
                    if(usingRawSocket && ntohs(reply->un.echo.id)!=(unsigned short)pid)
                        continue;

                    struct timeval now;
                    gettimeofday(&now,nullptr);
@@ -227,7 +239,7 @@ namespace fresh { namespace cmd {

                    if(!quiet)
                        std::cout << icmpLen << " bytes from " << ipbuf
                                   << ": icmp_seq=" << reply->un.echo.sequence
                                   << ": icmp_seq=" << ntohs(reply->un.echo.sequence)
                                   << " time=" << std::fixed << std::setprecision(3) << rtt
                                   << " ms" << std::endl;

+2 −1
Changes for commands/system/CMakeLists.txt: 2 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -30,9 +30,10 @@ target_sources(fresh PRIVATE
  ${CMAKE_CURRENT_SOURCE_DIR}/gzip.cpp
  ${CMAKE_CURRENT_SOURCE_DIR}/xz.cpp
  ${CMAKE_CURRENT_SOURCE_DIR}/nproc.cpp
  ${CMAKE_CURRENT_SOURCE_DIR}/dmesg.cpp
  ${CMAKE_CURRENT_SOURCE_DIR}/sysutils.cpp
)

set(FRESH_APPLETS ${FRESH_APPLETS} uname whoami hostname id date sleep env true false clear kill
    mount umount mknod switch_root cpio mkinitramfs losetup blkid
    halt reboot poweroff sysctl swapon swapoff pivot_root fsck mkswap hwclock ps tar gzip gunzip zcat xz nproc timeout tty logname PARENT_SCOPE)
    halt reboot poweroff sysctl swapon swapoff pivot_root fsck mkswap hwclock ps tar gzip gunzip zcat xz nproc dmesg timeout tty logname PARENT_SCOPE)
+10 −0
Changes for src/shell.cpp: 10 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -1141,7 +1141,17 @@ std::vector<fresh::ShellWord> fresh::tokenizeShellWords(const std::string &input
            if(input[i]=='\n') sawNewline=true;
            ++i;
        }
        // Nor after a pipe or "&&"/"||": a newline there is just a line
        // break inside the same pipeline/list (POSIX "linebreak"). parse()
        // joins such lines with a space when it pulls them itself, but a
        // command substitution's text arrives with the newline still in it
        // -- e.g. cmake's bootstrap script's
        //   cmake_copyright="`grep '^Copyright .* Kitware' ... |
        //                     sed 's/...//'`"
        // ran "grep ... |" and "sed" as two statements, sed reading the
        // shell's own stdin (a terminal: the build hung).
        if(sawNewline && !words.empty() && words.back().text!=";" &&
           !fresh::endsWithBareOperator(words.back().text) &&
           !(i<n && input[i]==';')){
            ShellWord sep;
            sep.text=";"; sep.start=i; sep.end=i;