1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
|
/*
* PuTTY miscellaneous Unix stuff
*/
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include "putty.h"
unsigned long getticks(void)
{
/*
* 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.
*/
#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 *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;
}
bool filename_equal(const Filename *f1, const Filename *f2)
{
return !strcmp(f1->path, f2->path);
}
bool filename_is_null(const Filename *fn)
{
return !fn->path[0];
}
void filename_free(Filename *fn)
{
sfree(fn->path);
sfree(fn);
}
void filename_serialise(BinarySink *bs, const Filename *f)
{
put_asciz(bs, f->path);
}
Filename *filename_deserialise(BinarySource *src)
{
return filename_from_str(get_asciz(src));
}
char filename_char_sanitise(char c)
{
if (c == '/')
return '.';
return c;
}
#ifdef DEBUG
static FILE *debug_fp = NULL;
void dputs(const char *buf)
{
if (!debug_fp) {
debug_fp = fopen("debug.log", "w");
}
if (write(1, buf, strlen(buf)) < 0) {} /* 'error check' to placate gcc */
fputs(buf, debug_fp);
fflush(debug_fp);
}
#endif
char *get_username(void)
{
struct passwd *p;
uid_t uid = getuid();
char *user, *ret = NULL;
/*
* First, find who we think we are using getlogin. If this
* agrees with our uid, we'll go along with it. This should
* allow sharing of uids between several login names whilst
* coping correctly with people who have su'ed.
*/
user = getlogin();
#if HAVE_SETPWENT
setpwent();
#endif
if (user)
p = getpwnam(user);
else
p = NULL;
if (p && p->pw_uid == uid) {
/*
* The result of getlogin() really does correspond to
* our uid. Fine.
*/
ret = user;
} else {
/*
* If that didn't work, for whatever reason, we'll do
* the simpler version: look up our uid in the password
* file and map it straight to a name.
*/
p = getpwuid(uid);
if (!p)
return NULL;
ret = p->pw_name;
}
#if HAVE_ENDPWENT
endpwent();
#endif
return dupstr(ret);
}
/*
* Display the fingerprints of the PGP Master Keys to the user.
* (This is here rather than in uxcons because it's appropriate even for
* Unix GUI apps.)
*/
void pgp_fingerprints(void)
{
fputs("These are the fingerprints of the PuTTY PGP Master Keys. They can\n"
"be used to establish a trust path from this executable to another\n"
"one. See the manual for more information.\n"
"(Note: these fingerprints have nothing to do with SSH!)\n"
"\n"
"PuTTY Master Key as of " PGP_MASTER_KEY_YEAR
" (" PGP_MASTER_KEY_DETAILS "):\n"
" " PGP_MASTER_KEY_FP "\n\n"
"Previous Master Key (" PGP_PREV_MASTER_KEY_YEAR
", " PGP_PREV_MASTER_KEY_DETAILS "):\n"
" " PGP_PREV_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);
}
}
bool 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;
}
bool 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, bool 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);
}
void fontspec_serialise(BinarySink *bs, FontSpec *f)
{
put_asciz(bs, f->name);
}
FontSpec *fontspec_deserialise(BinarySource *src)
{
return fontspec_new(get_asciz(src));
}
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;
}
char *make_dir_path(const char *path, mode_t mode)
{
int pos = 0;
char *prefix;
while (1) {
pos += strcspn(path + pos, "/");
if (pos > 0) {
prefix = dupprintf("%.*s", pos, path);
if (mkdir(prefix, mode) < 0 && errno != EEXIST) {
char *ret = dupprintf("%s: mkdir: %s",
prefix, strerror(errno));
sfree(prefix);
return ret;
}
sfree(prefix);
}
if (!path[pos])
return NULL;
pos += strspn(path + pos, "/");
}
}
bool open_for_write_would_lose_data(const Filename *fn)
{
struct stat st;
if (stat(fn->path, &st) < 0) {
/*
* If the file doesn't even exist, we obviously want to return
* false. If we failed to stat it for any other reason,
* ignoring the precise error code and returning false still
* doesn't seem too unreasonable, because then we'll try to
* open the file for writing and report _that_ error, which is
* likely to be more to the point.
*/
return false;
}
/*
* OK, something exists at this pathname and we've found out
* something about it. But an open-for-write will only
* destructively truncate it if it's a regular file with nonzero
* size. If it's empty, or some other kind of special thing like a
* character device (e.g. /dev/tty) or a named pipe, then opening
* it for write is already non-destructive and it's pointless and
* annoying to warn about it just because the same file can be
* opened for reading. (Indeed, if it's a named pipe, opening it
* for reading actually _causes inconvenience_ in its own right,
* even before the question of whether it gives misleading
* information.)
*/
if (S_ISREG(st.st_mode) && st.st_size > 0) {
return true;
}
return false;
}
|