Пример #1
0
static char *make_dirname(const char *pi_name, char **logtext)
{
    char *name, *parentdirname, *dirname, *err;

    /*
     * First, create the top-level directory for all shared PuTTY
     * connections owned by this user.
     */
    parentdirname = make_parentdir_name();
    if ((err = make_dir_and_check_ours(parentdirname)) != NULL) {
        *logtext = err;
        sfree(parentdirname);
        return NULL;
    }

    /*
     * Transform the platform-independent version of the connection
     * identifier into the name we'll actually use for the directory
     * containing the Unix socket.
     *
     * We do this by hashing the identifier with some user-specific
     * secret information, to avoid the privacy leak of having
     * "user@host" strings show up in 'netstat -x'. (Irritatingly, the
     * full pathname of a Unix-domain socket _does_ show up in the
     * 'netstat -x' output, at least on Linux, even if that socket is
     * in a directory not readable to the user running netstat. You'd
     * think putting things inside an 0700 directory would hide their
     * names from other users, but no.)
     *
     * The secret information we use to salt the hash lives in a file
     * inside the top-level directory we just created, so we must
     * first create that file (with some fresh random data in it) if
     * it's not already been done by a previous PuTTY.
     */
    {
        unsigned char saltbuf[SALT_SIZE];
        char *saltname;
        int saltfd, i, ret;

        saltname = dupprintf("%s/%s", parentdirname, SALT_FILENAME);
        saltfd = open(saltname, O_RDONLY);
        if (saltfd < 0) {
            char *tmpname;
            int pid;

            if (errno != ENOENT) {
                *logtext = dupprintf("%s: open: %s", saltname,
                                     strerror(errno));
                sfree(saltname);
                sfree(parentdirname);
                return NULL;
            }

            /*
             * The salt file doesn't already exist, so try to create
             * it. Another process may be attempting the same thing
             * simultaneously, so we must do this carefully: we write
             * a salt file under a different name, then hard-link it
             * into place, which guarantees that we won't change the
             * contents of an existing salt file.
             */
            pid = getpid();
            for (i = 0;; i++) {
                tmpname = dupprintf("%s/%s.tmp.%d.%d",
                                    parentdirname, SALT_FILENAME, pid, i);
                saltfd = open(tmpname, O_WRONLY | O_EXCL | O_CREAT, 0400);
                if (saltfd >= 0)
                    break;
                if (errno != EEXIST) {
                    *logtext = dupprintf("%s: open: %s", tmpname,
                                         strerror(errno));
                    sfree(tmpname);
                    sfree(saltname);
                    sfree(parentdirname);
                    return NULL;
                }
                sfree(tmpname);        /* go round and try again with i+1 */
            }
            /*
             * Invent some random data.
             */
            for (i = 0; i < SALT_SIZE; i++) {
                saltbuf[i] = random_byte();
            }
            ret = write(saltfd, saltbuf, SALT_SIZE);
            /* POSIX atomicity guarantee: because we wrote less than
             * PIPE_BUF bytes, the write either completed in full or
             * failed. */
            assert(SALT_SIZE < PIPE_BUF);
            assert(ret < 0 || ret == SALT_SIZE);
            if (ret < 0) {
                close(saltfd);
                *logtext = dupprintf("%s: write: %s", tmpname,
                                     strerror(errno));
                sfree(tmpname);
                sfree(saltname);
                sfree(parentdirname);
                return NULL;
            }
            if (close(saltfd) < 0) {
                *logtext = dupprintf("%s: close: %s", tmpname,
                                     strerror(errno));
                sfree(tmpname);
                sfree(saltname);
                sfree(parentdirname);
                return NULL;
            }

            /*
             * Now attempt to hard-link our temp file into place. We
             * tolerate EEXIST as an outcome, because that just means
             * another PuTTY got their attempt in before we did (and
             * we only care that there is a valid salt file we can
             * agree on, no matter who created it).
             */
            if (link(tmpname, saltname) < 0 && errno != EEXIST) {
                *logtext = dupprintf("%s: link: %s", saltname,
                                     strerror(errno));
                sfree(tmpname);
                sfree(saltname);
                sfree(parentdirname);
                return NULL;
            }

            /*
             * Whether that succeeded or not, get rid of our temp file.
             */
            if (unlink(tmpname) < 0) {
                *logtext = dupprintf("%s: unlink: %s", tmpname,
                                     strerror(errno));
                sfree(tmpname);
                sfree(saltname);
                sfree(parentdirname);
                return NULL;
            }

            /*
             * And now we've arranged for there to be a salt file, so
             * we can try to open it for reading again and this time
             * expect it to work.
             */
            sfree(tmpname);

            saltfd = open(saltname, O_RDONLY);
            if (saltfd < 0) {
                *logtext = dupprintf("%s: open: %s", saltname,
                                     strerror(errno));
                sfree(saltname);
                sfree(parentdirname);
                return NULL;
            }
        }

        for (i = 0; i < SALT_SIZE; i++) {
            ret = read(saltfd, saltbuf, SALT_SIZE);
            if (ret <= 0) {
                close(saltfd);
                *logtext = dupprintf("%s: read: %s", saltname,
                                     ret == 0 ? "unexpected EOF" :
                                     strerror(errno));
                sfree(saltname);
                sfree(parentdirname);
                return NULL;
            }
            assert(0 < ret && ret <= SALT_SIZE - i);
            i += ret;
        }

        close(saltfd);
        sfree(saltname);

        /*
         * Now we've got our salt, hash it with the connection
         * identifier to produce our actual socket name.
         */
        {
            SHA256_State sha;
            unsigned len;
            unsigned char lenbuf[4];
            unsigned char digest[32];
            char retbuf[65];

            SHA256_Init(&sha);
            PUT_32BIT(lenbuf, SALT_SIZE);
            SHA256_Bytes(&sha, lenbuf, 4);
            SHA256_Bytes(&sha, saltbuf, SALT_SIZE);
            len = strlen(pi_name);
            PUT_32BIT(lenbuf, len);
            SHA256_Bytes(&sha, lenbuf, 4);
            SHA256_Bytes(&sha, pi_name, len);
            SHA256_Final(&sha, digest);

            /*
             * And make it printable.
             */
            for (i = 0; i < 32; i++) {
                sprintf(retbuf + 2*i, "%02x", digest[i]);
                /* the last of those will also write the trailing NUL */
            }

            name = dupstr(retbuf);
        }

        smemclr(saltbuf, sizeof(saltbuf));
    }

    dirname = dupprintf("%s/%s", parentdirname, name);
    sfree(parentdirname);
    sfree(name);

    return dirname;
}
Пример #2
0
static char *obfuscate_name(const char *realname)
{
    /*
     * Windows's named pipes all live in the same namespace, so one
     * user can see what pipes another user has open. This is an
     * undesirable privacy leak and in particular permits one user to
     * know what username@host another user is SSHing to, so we
     * protect that information by using CryptProtectMemory (which
     * uses a key built in to each user's account).
     */
    char *cryptdata;
    int cryptlen;
    SHA256_State sha;
    unsigned char lenbuf[4];
    unsigned char digest[32];
    char retbuf[65];
    int i;

    cryptlen = strlen(realname) + 1;
    cryptlen += CRYPTPROTECTMEMORY_BLOCK_SIZE - 1;
    cryptlen /= CRYPTPROTECTMEMORY_BLOCK_SIZE;
    cryptlen *= CRYPTPROTECTMEMORY_BLOCK_SIZE;

    cryptdata = snewn(cryptlen, char);
    memset(cryptdata, 0, cryptlen);
    strcpy(cryptdata, realname);

    /*
     * CRYPTPROTECTMEMORY_CROSS_PROCESS causes CryptProtectMemory to
     * use the same key in all processes with this user id, meaning
     * that the next PuTTY process calling this function with the same
     * input will get the same data.
     *
     * (Contrast with CryptProtectData, which invents a new session
     * key every time since its API permits returning more data than
     * was input, so calling _that_ and hashing the output would not
     * be stable.)
     *
     * We don't worry too much if this doesn't work for some reason.
     * Omitting this step still has _some_ privacy value (in that
     * another user can test-hash things to confirm guesses as to
     * where you might be connecting to, but cannot invert SHA-256 in
     * the absence of any plausible guess). So we don't abort if we
     * can't call CryptProtectMemory at all, or if it fails.
     */
    if (got_crypt())
        p_CryptProtectMemory(cryptdata, cryptlen,
                             CRYPTPROTECTMEMORY_CROSS_PROCESS);

    /*
     * We don't want to give away the length of the hostname either,
     * so having got it back out of CryptProtectMemory we now hash it.
     */
    SHA256_Init(&sha);
    PUT_32BIT_MSB_FIRST(lenbuf, cryptlen);
    SHA256_Bytes(&sha, lenbuf, 4);
    SHA256_Bytes(&sha, cryptdata, cryptlen);
    SHA256_Final(&sha, digest);

    sfree(cryptdata);

    /*
     * Finally, make printable.
     */
    for (i = 0; i < 32; i++) {
        sprintf(retbuf + 2*i, "%02x", digest[i]);
        /* the last of those will also write the trailing NUL */
    }

    return dupstr(retbuf);
}
Пример #3
0
void SHA224_Bytes(SHA256_State *s, const void *p, int len) {
    SHA256_Bytes(s, p, len);
}