X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=unix%2Fuxmisc.c;h=a7a2fcb93576bf7248a409757458a41fdbe0b827;hb=c2c22fb16a87783d26edd3235ea9b0d3c6f414e1;hp=c613a204f5b779714f9cac13314676b01c09c00f;hpb=d0beed9aba20865b9829e3203b4067e8822a0f4d;p=PuTTY.git diff --git a/unix/uxmisc.c b/unix/uxmisc.c index c613a204..a7a2fcb9 100644 --- a/unix/uxmisc.c +++ b/unix/uxmisc.c @@ -2,62 +2,116 @@ * PuTTY miscellaneous Unix stuff */ +#include #include #include +#include +#include #include +#include #include #include +#include #include #include "putty.h" -long tickcount_offset = 0; - unsigned long getticks(void) { - struct timeval tv; - gettimeofday(&tv, NULL); /* - * We want to use milliseconds rather than microseconds, - * because we need a decent number of them to fit into a 32-bit - * word so it can be used for keepalives. + * We want to use milliseconds rather than the microseconds or + * nanoseconds given by the underlying clock functions, because we + * need a decent number of them to fit into a 32-bit word so it + * can be used for keepalives. */ - return tv.tv_sec * 1000 + tv.tv_usec / 1000 + tickcount_offset; +#if defined HAVE_CLOCK_GETTIME && defined HAVE_DECL_CLOCK_MONOTONIC + { + /* Use CLOCK_MONOTONIC if available, so as to be unconfused if + * the system clock changes. */ + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + return ts.tv_sec * TICKSPERSEC + + ts.tv_nsec / (1000000000 / TICKSPERSEC); + } +#endif + { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * TICKSPERSEC + tv.tv_usec / (1000000 / TICKSPERSEC); + } } -Filename filename_from_str(const char *str) +Filename *filename_from_str(const char *str) { - Filename ret; - strncpy(ret.path, str, sizeof(ret.path)); - ret.path[sizeof(ret.path)-1] = '\0'; + Filename *ret = snew(Filename); + ret->path = dupstr(str); return ret; } +Filename *filename_copy(const Filename *fn) +{ + return filename_from_str(fn->path); +} + const char *filename_to_str(const Filename *fn) { return fn->path; } -int filename_equal(Filename f1, Filename f2) +int filename_equal(const Filename *f1, const Filename *f2) +{ + return !strcmp(f1->path, f2->path); +} + +int filename_is_null(const Filename *fn) +{ + return !fn->path[0]; +} + +void filename_free(Filename *fn) +{ + sfree(fn->path); + sfree(fn); +} + +int filename_serialise(const Filename *f, void *vdata) +{ + char *data = (char *)vdata; + int len = strlen(f->path) + 1; /* include trailing NUL */ + if (data) { + strcpy(data, f->path); + } + return len; +} +Filename *filename_deserialise(void *vdata, int maxsize, int *used) { - return !strcmp(f1.path, f2.path); + char *data = (char *)vdata; + char *end; + end = memchr(data, '\0', maxsize); + if (!end) + return NULL; + end++; + *used = end - data; + return filename_from_str(data); } -int filename_is_null(Filename fn) +char filename_char_sanitise(char c) { - return !*fn.path; + if (c == '/') + return '.'; + return c; } #ifdef DEBUG static FILE *debug_fp = NULL; -void dputs(char *buf) +void dputs(const char *buf) { if (!debug_fp) { debug_fp = fopen("debug.log", "w"); } - write(1, buf, strlen(buf)); + if (write(1, buf, strlen(buf)) < 0) {} /* 'error check' to placate gcc */ fputs(buf, debug_fp); fflush(debug_fp); @@ -116,8 +170,155 @@ void pgp_fingerprints(void) "one. See the manual for more information.\n" "(Note: these fingerprints have nothing to do with SSH!)\n" "\n" - "PuTTY Master Key (RSA), 1024-bit:\n" + "PuTTY Master Key as of 2015 (RSA, 4096-bit):\n" + " " PGP_MASTER_KEY_FP "\n\n" + "Original PuTTY Master Key (RSA, 1024-bit):\n" " " PGP_RSA_MASTER_KEY_FP "\n" - "PuTTY Master Key (DSA), 1024-bit:\n" + "Original PuTTY Master Key (DSA, 1024-bit):\n" " " PGP_DSA_MASTER_KEY_FP "\n", stdout); } + +/* + * Set and clear fcntl options on a file descriptor. We don't + * realistically expect any of these operations to fail (the most + * plausible error condition is EBADF, but we always believe ourselves + * to be passing a valid fd so even that's an assertion-fail sort of + * response), so we don't make any effort to return sensible error + * codes to the caller - we just log to standard error and die + * unceremoniously. However, nonblock and no_nonblock do return the + * previous state of O_NONBLOCK. + */ +void cloexec(int fd) { + int fdflags; + + fdflags = fcntl(fd, F_GETFD); + if (fdflags < 0) { + fprintf(stderr, "%d: fcntl(F_GETFD): %s\n", fd, strerror(errno)); + exit(1); + } + if (fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC) < 0) { + fprintf(stderr, "%d: fcntl(F_SETFD): %s\n", fd, strerror(errno)); + exit(1); + } +} +void noncloexec(int fd) { + int fdflags; + + fdflags = fcntl(fd, F_GETFD); + if (fdflags < 0) { + fprintf(stderr, "%d: fcntl(F_GETFD): %s\n", fd, strerror(errno)); + exit(1); + } + if (fcntl(fd, F_SETFD, fdflags & ~FD_CLOEXEC) < 0) { + fprintf(stderr, "%d: fcntl(F_SETFD): %s\n", fd, strerror(errno)); + exit(1); + } +} +int nonblock(int fd) { + int fdflags; + + fdflags = fcntl(fd, F_GETFL); + if (fdflags < 0) { + fprintf(stderr, "%d: fcntl(F_GETFL): %s\n", fd, strerror(errno)); + exit(1); + } + if (fcntl(fd, F_SETFL, fdflags | O_NONBLOCK) < 0) { + fprintf(stderr, "%d: fcntl(F_SETFL): %s\n", fd, strerror(errno)); + exit(1); + } + + return fdflags & O_NONBLOCK; +} +int no_nonblock(int fd) { + int fdflags; + + fdflags = fcntl(fd, F_GETFL); + if (fdflags < 0) { + fprintf(stderr, "%d: fcntl(F_GETFL): %s\n", fd, strerror(errno)); + exit(1); + } + if (fcntl(fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) { + fprintf(stderr, "%d: fcntl(F_SETFL): %s\n", fd, strerror(errno)); + exit(1); + } + + return fdflags & O_NONBLOCK; +} + +FILE *f_open(const Filename *filename, char const *mode, int is_private) +{ + if (!is_private) { + return fopen(filename->path, mode); + } else { + int fd; + assert(mode[0] == 'w'); /* is_private is meaningless for read, + and tricky for append */ + fd = open(filename->path, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) + return NULL; + return fdopen(fd, mode); + } +} + +FontSpec *fontspec_new(const char *name) +{ + FontSpec *f = snew(FontSpec); + f->name = dupstr(name); + return f; +} +FontSpec *fontspec_copy(const FontSpec *f) +{ + return fontspec_new(f->name); +} +void fontspec_free(FontSpec *f) +{ + sfree(f->name); + sfree(f); +} +int fontspec_serialise(FontSpec *f, void *data) +{ + int len = strlen(f->name); + if (data) + strcpy(data, f->name); + return len + 1; /* include trailing NUL */ +} +FontSpec *fontspec_deserialise(void *vdata, int maxsize, int *used) +{ + char *data = (char *)vdata; + char *end = memchr(data, '\0', maxsize); + if (!end) + return NULL; + *used = end - data + 1; + return fontspec_new(data); +} + +char *make_dir_and_check_ours(const char *dirname) +{ + struct stat st; + + /* + * Create the directory. We might have created it before, so + * EEXIST is an OK error; but anything else is doom. + */ + if (mkdir(dirname, 0700) < 0 && errno != EEXIST) + return dupprintf("%s: mkdir: %s", dirname, strerror(errno)); + + /* + * Now check that that directory is _owned by us_ and not writable + * by anybody else. This protects us against somebody else + * previously having created the directory in a way that's + * writable to us, and thus manipulating us into creating the + * actual socket in a directory they can see so that they can + * connect to it and use our authenticated SSH sessions. + */ + if (stat(dirname, &st) < 0) + return dupprintf("%s: stat: %s", dirname, strerror(errno)); + if (st.st_uid != getuid()) + return dupprintf("%s: directory owned by uid %d, not by us", + dirname, st.st_uid); + if ((st.st_mode & 077) != 0) + return dupprintf("%s: directory has overgenerous permissions %03o" + " (expected 700)", dirname, st.st_mode & 0777); + + return NULL; +}