~ubuntu-branches/ubuntu/trusty/libpam-mount/trusty-proposed

« back to all changes in this revision

Viewing changes to .pc/hurd-path-max-define/src/pmvarrun.c

  • Committer: Bazaar Package Importer
  • Author(s): Bastian Kleineidam
  • Date: 2010-12-03 20:27:54 UTC
  • mfrom: (1.4.8 upstream)
  • Revision ID: james.westby@ubuntu.com-20101203202754-ki2m85culrxwklox
Tags: 2.7-1
* New upstream release.
  + Remove mnt_fallback patch since squeeze will have 2.5 and upgrading
    from 0.4x versions is not needed anymore.
  + Readd copy of ofl and fd0ssh to avoid installing a complete new
    package.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *      pam_mount
 
3
 *      Copyright © W. Michael Petullo <mike@flyn.org>, 2004
 
4
 *      Copyright © Jan Engelhardt, 2005 - 2008
 
5
 *      Copyright © Bastian Kleineidam <calvin [at] debian org>, 2005
 
6
 *
 
7
 *      This file is part of pam_mount; you can redistribute it and/or
 
8
 *      modify it under the terms of the GNU Lesser General Public License
 
9
 *      as published by the Free Software Foundation; either version 2.1
 
10
 *      of the License, or (at your option) any later version.
 
11
 */
 
12
/*
 
13
pmvarrun.c -- Updates /var/run/pam_mount/<user>.
 
14
    A seperate program is needed so that /var/run/pam_mount/<user> may be
 
15
    created with a pam_mount-specific security context (otherwise SELinux
 
16
    policy will conflict with gdm, which also creates files in /var/run).
 
17
*/
 
18
 
 
19
#include <sys/stat.h>
 
20
#include <sys/types.h>
 
21
#include <assert.h>
 
22
#include <errno.h>
 
23
#include <fcntl.h>
 
24
#include <getopt.h>
 
25
#include <limits.h>
 
26
#include <stdbool.h>
 
27
#include <stdio.h>
 
28
#include <stdlib.h>
 
29
#include <string.h>
 
30
#include <unistd.h>
 
31
#include <libHX/defs.h>
 
32
#include <libHX/string.h>
 
33
#include <pwd.h>
 
34
#include "pam_mount.h"
 
35
 
 
36
/* Definitions */
 
37
#define ASCIIZ_LLX      sizeof("0xFFFFFFFF""FFFFFFFF")
 
38
#define VAR_RUN         "/var/run"
 
39
#define VAR_RUN_PMT     VAR_RUN "/pam_mount"
 
40
 
 
41
struct settings {
 
42
        char *user;
 
43
        long operation;
 
44
};
 
45
 
 
46
/* Functions */
 
47
static int create_var_run(void);
 
48
static int modify_pm_count(const char *, long);
 
49
static int open_and_lock(const char *, long);
 
50
static void parse_args(const int, const char **, struct settings *);
 
51
static long read_current_count(int, const char *);
 
52
static void set_defaults(struct settings *);
 
53
static void usage(int, const char *);
 
54
static int write_count(int, long, const char *);
 
55
 
 
56
/**
 
57
 * usage - display help
 
58
 * @exitcode:   numeric value we will be exiting with
 
59
 * @error:      descriptive error string
 
60
 *
 
61
 * Displays the help string and an optional extra error message.
 
62
 */
 
63
static void usage(const int exitcode, const char *error)
 
64
{
 
65
        fprintf(stderr, "Usage: pmvarrun -u USER [-o NUMBER] [-d]\n");
 
66
        if (error != NULL)
 
67
                fprintf(stderr, "%s\n\n", error);
 
68
        exit(exitcode);
 
69
}
 
70
 
 
71
/**
 
72
 * set_defaults -
 
73
 * @settings:   pointer to settings structure
 
74
 */
 
75
static void set_defaults(struct settings *settings)
 
76
{
 
77
        const char *s;
 
78
 
 
79
        settings->user      = NULL;
 
80
        settings->operation = 1;
 
81
 
 
82
        if ((s = getenv("_PMT_DEBUG_LEVEL")) != NULL) {
 
83
                Debug = strtoul(s, NULL, 0);
 
84
                pmtlog_path[PMTLOG_ERR][PMTLOG_STDERR] = Debug;
 
85
                pmtlog_path[PMTLOG_DBG][PMTLOG_STDERR] = Debug;
 
86
        }
 
87
}
 
88
 
 
89
/*
 
90
 * from git://dev.medozas.de/vitalnix /src/libvxutil/util.c
 
91
 */
 
92
static bool valid_username(const char *n)
 
93
{
 
94
        if (*n == '\0')
 
95
                return false;
 
96
 
 
97
        /*
 
98
         * Note to future editors: Some systems disallow leading digits for
 
99
         * usernames. Possibly because it is concerned about badly-written
 
100
         * programs detecting numeric UIDs by merely doing
 
101
         * strtoul(s, &e, 0) && s != e, which falls victim to usernames
 
102
         * with leading digits.
 
103
         * So pam_mount, in its original form at least (distros can patch
 
104
         * their copy up as they see fit), should at best reject
 
105
         * leading digits too.
 
106
         */
 
107
        /*
 
108
         * Cannot use isalpha/isdigit here since that may include
 
109
         * more characters.
 
110
         */
 
111
        if (!((*n >= 'A' && *n <= 'Z') || (*n >= 'a' && *n <= 'z') ||
 
112
            *n == '_'))
 
113
                return false;
 
114
 
 
115
        while (*n != '\0') {
 
116
                bool valid;
 
117
 
 
118
                if (*n == '$' && *(n+1) == '\0') /* Samba accounts */
 
119
                        return true;
 
120
 
 
121
                valid = (*n >= 'A' && *n <= 'Z') || (*n >= 'a' && *n <= 'z') ||
 
122
                        (*n >= '0' && *n <= '9') || *n == '_' || *n == '.' ||
 
123
                        *n == '-' || *n == ' ' || *n == '\\';
 
124
                if (!valid)
 
125
                        return false;
 
126
                ++n;
 
127
        }
 
128
 
 
129
        return true;
 
130
}
 
131
 
 
132
/**
 
133
 * str_to_long -
 
134
 * @n:  string to analyze
 
135
 *
 
136
 * Calls @strtol on @n using base 10 and makes sure there were no invalid
 
137
 * characters in @n. Returns the value, or %LONG_MAX in case of an
 
138
 * over-/underflow.
 
139
 * NOTE: This function is only referenced from pmvarrun.c.
 
140
 */
 
141
long str_to_long(const char *n)
 
142
{
 
143
        long val;
 
144
        char *endptr = NULL;
 
145
        if (n == NULL) {
 
146
                l0g("count string is NULL\n");
 
147
                return LONG_MAX;
 
148
        }
 
149
        val = strtol(n, &endptr, 10);
 
150
        if (*endptr != '\0') {
 
151
                l0g("count string is not valid\n");
 
152
                return LONG_MAX;
 
153
        }
 
154
        return val;
 
155
}
 
156
 
 
157
/**
 
158
 * parse_args -
 
159
 * @argc:       number of elements in @argv
 
160
 * @argv:       NULL-terminated argument vector
 
161
 * @settings:   pointer to settings structure
 
162
 *
 
163
 * Parse options from @argv and put it into @settings.
 
164
 */
 
165
static void parse_args(int argc, const char **argv, struct settings *settings)
 
166
{
 
167
        int c;
 
168
 
 
169
        while ((c = getopt(argc, const_cast2(char * const *, argv),
 
170
            "hdo:u:")) >= 0) {
 
171
                switch (c) {
 
172
                case 'h':
 
173
                        usage(EXIT_SUCCESS, NULL);
 
174
                        break;
 
175
                case 'd':
 
176
                        Debug = true;
 
177
                        break;
 
178
                case 'o':
 
179
                        settings->operation = str_to_long(optarg);
 
180
                        if (settings->operation == LONG_MAX ||
 
181
                            settings->operation == LONG_MIN)
 
182
                                usage(EXIT_FAILURE, "count string is not valid");
 
183
                        break;
 
184
                case 'u':
 
185
                        if (!valid_username(optarg)) {
 
186
                                fprintf(stderr, "Invalid user name\n");
 
187
                                exit(EXIT_FAILURE);
 
188
                        }
 
189
                        if ((settings->user = HX_strdup(optarg)) == NULL)
 
190
                                perror("malloc");
 
191
                        break;
 
192
                default:
 
193
                        usage(EXIT_FAILURE, NULL);
 
194
                        break;
 
195
                }
 
196
        }
 
197
}
 
198
 
 
199
/**
 
200
 * modify_pm_count -
 
201
 * @user:       user to poke on
 
202
 * @amount:     increment (usually -1, 0 or +1)
 
203
 *
 
204
 * Adjusts /var/run/pam_mount/@user by @amount, or deletes the file if the
 
205
 * resulting value (current + @amount) is <= 0. Returns >= 0 on success to
 
206
 * indicate the new login count, or negative to indicate errno. -ESTALE and
 
207
 * -EOVERFLOW are passed up from subfunctions and must be handled in the
 
208
 * caller.
 
209
 */
 
210
static int modify_pm_count(const char *user, long amount)
 
211
{
 
212
        char filename[PATH_MAX + 1];
 
213
        struct passwd *pent;
 
214
        struct stat sb;
 
215
        int fd, ret;
 
216
        long val;
 
217
 
 
218
        assert(user != NULL);
 
219
 
 
220
        if ((pent = getpwnam(user)) == NULL) {
 
221
                ret = -errno;
 
222
                l0g("could not resolve user %s\n", user);
 
223
                return ret;
 
224
        }
 
225
 
 
226
        if (stat(VAR_RUN_PMT, &sb) < 0) {
 
227
                if (errno != ENOENT) {
 
228
                        ret = -errno;
 
229
                        l0g("unable to stat " VAR_RUN_PMT ": %s\n",
 
230
                            strerror(errno));
 
231
                        return ret;
 
232
                }
 
233
                if ((ret = create_var_run()) < 0)
 
234
                        return ret;
 
235
        }
 
236
 
 
237
        snprintf(filename, sizeof(filename), VAR_RUN_PMT "/%s", user);
 
238
        while ((ret = fd = open_and_lock(filename, pent->pw_uid)) == -EAGAIN)
 
239
                /* noop */;
 
240
        if (ret < 0)
 
241
                return ret;
 
242
 
 
243
        if ((val = read_current_count(fd, filename)) < 0) {
 
244
                close(fd);
 
245
                return val;
 
246
        }
 
247
 
 
248
        w4rn("parsed count value %ld\n", val);
 
249
        /* amount == 0 implies query */
 
250
        ret = 1;
 
251
        if (amount != 0)
 
252
                ret = write_count(fd, val + amount, filename);
 
253
 
 
254
        close(fd);
 
255
        return (ret < 0) ? ret : val + amount;
 
256
}
 
257
 
 
258
int main(int argc, const char **argv)
 
259
{
 
260
        struct settings settings;
 
261
        int ret;
 
262
 
 
263
        Debug = false;
 
264
        /* pam_mount.so will pick stderr up */
 
265
        pmtlog_path[PMTLOG_ERR][PMTLOG_STDERR] = true;
 
266
        pmtlog_path[PMTLOG_DBG][PMTLOG_STDERR] = Debug;
 
267
        pmtlog_prefix = "pmvarrun";
 
268
 
 
269
        set_defaults(&settings);
 
270
        parse_args(argc, argv, &settings);
 
271
 
 
272
        if (settings.user == NULL)
 
273
                usage(EXIT_FAILURE, NULL);
 
274
 
 
275
        ret = modify_pm_count(settings.user, settings.operation);
 
276
        if (ret == -ESTALE) {
 
277
                printf("0\n");
 
278
                return EXIT_SUCCESS;
 
279
        } else if (ret < 0) {
 
280
                return EXIT_FAILURE;
 
281
        }
 
282
 
 
283
        /* print current count so pam_mount module may read it */
 
284
        printf("%d\n", ret);
 
285
        return EXIT_SUCCESS;
 
286
}
 
287
 
 
288
//-----------------------------------------------------------------------------
 
289
/**
 
290
 * create_var_run -
 
291
 *
 
292
 * Creates the /var/run/pam_mount directory required by pmvarrun and sets
 
293
 * proper permissions on it.
 
294
 *
 
295
 * Returns >0 for success or <=0 to indicate errno.
 
296
 */
 
297
static int create_var_run(void)
 
298
{
 
299
        int ret;
 
300
 
 
301
        w4rn("creating " VAR_RUN_PMT);
 
302
        if (mkdir(VAR_RUN_PMT, 0000) < 0) {
 
303
                ret = -errno;
 
304
                l0g("unable to create " VAR_RUN_PMT ": %s\n", strerror(errno));
 
305
                return ret;
 
306
        }
 
307
        if (chown(VAR_RUN_PMT, 0, 0) < 0) {
 
308
                ret = -errno;
 
309
                l0g("unable to chown " VAR_RUN_PMT ": %s\n", strerror(errno));
 
310
                return ret;
 
311
        }
 
312
 
 
313
        /*
 
314
         * 0755: `su` creates file group owned by user and then releases root
 
315
         * permissions. User needs to be able to access file on logout.
 
316
         */
 
317
        if (chmod(VAR_RUN_PMT, S_IRWXU | S_IRXG | S_IRXO) < 0) {
 
318
                ret = -errno;
 
319
                l0g("unable to chmod " VAR_RUN_PMT ": %s\n", strerror(errno));
 
320
                return ret;
 
321
        }
 
322
 
 
323
        return 1;
 
324
}
 
325
 
 
326
/**
 
327
 * open_and_lock -
 
328
 * @filename:   file to open
 
329
 *
 
330
 * Creates if necessary, opens and chown()s @filename, and locks it.
 
331
 * Returns the fd if all of that succeeded, -EAGAIN if the file was unlinked
 
332
 * during operation (see below), -ESTALE if the lock could not be obtained,
 
333
 * and <0 otherwise to indicate errno.
 
334
 */
 
335
static int open_and_lock(const char *filename, long uid) {
 
336
        struct flock lockinfo = {
 
337
                .l_type   = F_WRLCK,
 
338
                .l_whence = SEEK_SET,
 
339
                .l_start  = 0,
 
340
                .l_len  = 0,
 
341
        };
 
342
        struct stat sb;
 
343
        int fd, ret;
 
344
 
 
345
        if ((fd = open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
 
346
                ret = -errno;
 
347
                l0g("unable to open %s: %s\n", filename, strerror(errno));
 
348
                return ret;
 
349
        }
 
350
        if (fchown(fd, uid, 0) < 0) {
 
351
                ret = -errno;
 
352
                l0g("unable to chown %s: %s\n", filename, strerror(errno));
 
353
                return ret;
 
354
        }
 
355
 
 
356
        /*
 
357
         * Note: Waiting too long might interfere with LOGIN_TIMEOUT from
 
358
         * /etc/login.defs, and /bin/login itself may prematurely kill the
 
359
         * /session.
 
360
         */
 
361
        alarm(20);
 
362
        ret = fcntl(fd, F_SETLKW, &lockinfo);
 
363
        alarm(0);
 
364
        if (ret == EAGAIN) {
 
365
                /*
 
366
                 * [Flyn] If someone has locked the file and not written to it
 
367
                 * in at least 20 seconds, we assume they either forgot to
 
368
                 * unlock it or are catatonic -- chances are slim that they are
 
369
                 * in the middle of a read-write cycle and I do not want to
 
370
                 * make us lock users out. Perhaps I should just return
 
371
                 * %PAM_SUCCESS instead and log the event? Kill the process
 
372
                 * holding the lock? Options abound... For now, we ignore it.
 
373
                 *
 
374
                 * [CCJ] pmvarrun is the only one ever writing to that file,
 
375
                 * and we keep the lock as short as possible. So if there is no
 
376
                 * response within the time limit, something is fouled up (e.g. 
 
377
                 * NFS server not responding -- though /var/run should at best
 
378
                 * not be on an NFS mount).  Continue, let user log in, do not
 
379
                 * change anything.
 
380
                 */
 
381
                w4rn("stale lock on file %s - continuing without increasing"
 
382
                     "pam_mount reference count\n", filename);
 
383
                close(fd);
 
384
                return -ESTALE;
 
385
        }
 
386
 
 
387
        /*
 
388
         * It is possible at this point that the file has been removed by a
 
389
         * previous login; if this happens, we need to start over.
 
390
         */
 
391
        if (stat(filename, &sb) < 0) {
 
392
                ret = -errno;
 
393
                close(fd);
 
394
                if (ret == -ENOENT)
 
395
                        return -EAGAIN;
 
396
                return ret;
 
397
        }
 
398
 
 
399
        return fd;
 
400
}
 
401
 
 
402
/**
 
403
 * read_current_count -
 
404
 * @fd: file descriptor to read from
 
405
 *
 
406
 * Reads the current user's reference count from @fd and returns the value
 
407
 * on success. Otherwise, returns -EOVERFLOW in case we suspect a problem or
 
408
 * <0 to indicate errno.
 
409
 */
 
410
static long read_current_count(int fd, const char *filename) {
 
411
        char buf[ASCIIZ_LLX] = {};
 
412
        long ret;
 
413
 
 
414
        if ((ret = read(fd, buf, sizeof(buf))) < 0) {
 
415
                ret = -errno;
 
416
                l0g("read error on %s: %s\n", filename, strerror(errno));
 
417
                close(fd);
 
418
                return ret;
 
419
        } else if (ret == 0) {
 
420
                /* File is empty, ret is already 0 -- we are set. */
 
421
        } else if (ret < sizeof(buf)) {
 
422
                char *p;
 
423
                if ((ret = strtol(buf, &p, 0)) >= LONG_MAX || p == buf) {
 
424
                        l0g("parse problem / session count corrupt "
 
425
                            "(overflow), check your refcount file\n");
 
426
                        return -EOVERFLOW;
 
427
                }
 
428
        } else if (ret >= sizeof(buf)) {
 
429
                l0g("session count corrupt (overflow)\n");
 
430
                return -EOVERFLOW;
 
431
        }
 
432
 
 
433
        return ret;
 
434
}
 
435
 
 
436
/**
 
437
 * write_count -
 
438
 * @fd:         file descriptor to write to
 
439
 * @nv:         new value to write
 
440
 * @filename:   filename, only used for l0g()
 
441
 *
 
442
 * Writes @nv as a number in hexadecimal to the start of the file @fd and
 
443
 * truncates the file to the written length.
 
444
 */
 
445
static int write_count(int fd, long nv, const char *filename) {
 
446
        char buf[ASCIIZ_LLX];
 
447
        int wrt, len, ret;
 
448
 
 
449
        if (nv <= 0) {
 
450
                if (unlink(filename) >= 0)
 
451
                        return true;
 
452
                if (errno != EPERM)
 
453
                        l0g("could not unlink %s: %s\n", filename, strerror(errno));
 
454
                /*
 
455
                 * Fallback to just blanking the file. This can happen when
 
456
                 * pmvarrun is called as unprivileged user.
 
457
                 */
 
458
                if (ftruncate(fd, 0) < 0)
 
459
                        w4rn("truncate failed: %s\n", strerror(errno));
 
460
                return true;
 
461
        }
 
462
 
 
463
        if ((ret = lseek(fd, 0, SEEK_SET)) != 0) {
 
464
                ret = -errno;
 
465
                l0g("failed to seek in %s: %s\n", filename, strerror(errno));
 
466
                return ret;
 
467
        }
 
468
 
 
469
        len = snprintf(buf, sizeof(buf), "0x%lX", nv);
 
470
        if ((wrt = write(fd, buf, len)) != len) {
 
471
                ret = -errno;
 
472
                l0g("wrote %d of %d bytes; write error on %s: %s\n",
 
473
                    (wrt < 0) ? 0 : wrt, len, filename, strerror(errno));
 
474
                return ret;
 
475
        }
 
476
 
 
477
        if (ftruncate(fd, len) < 0) {
 
478
                ret = -errno;
 
479
                l0g("truncate failed: %s\n", strerror(errno));
 
480
                return ret;
 
481
        }
 
482
 
 
483
        return 1;
 
484
}