root/trunk/src/apps/admin/pvfs2-gencred.c @ 9170

Revision 9170, 10.1 KB (checked in by sampson, 16 months ago)

Client programs no longer run pvfs2-gencred if security isn't enabled.

Line 
1/*
2 * Copyright 2010 Clemson University and The University of Chicago.
3 *
4 * See COPYING in top-level directory.
5 */
6
7#include "pvfs2-config.h"
8#include <stdlib.h>
9#include <stdio.h>
10#include <string.h>
11#include <assert.h>
12#include <sys/types.h>
13#include <sys/stat.h>
14#include <fcntl.h>
15#include <errno.h>
16#include <time.h>
17#include <pwd.h>
18#include <grp.h>
19#include <unistd.h>
20
21/* FIXME: obtaining HOST_NAME_MAX is platform specific and should be handled more generally */
22#ifndef HOST_NAME_MAX
23#define HOST_NAME_MAX 64
24#endif
25
26#ifdef ENABLE_SECURITY
27#include <openssl/err.h>
28#include <openssl/evp.h>
29#include <openssl/pem.h>
30#endif
31
32#define __PINT_REQPROTO_ENCODE_FUNCS_C
33#include "pvfs2-types.h"
34#include "src/proto/pvfs2-req-proto.h"
35#include "src/common/security/getugroups.h"
36
37
38typedef struct {
39    const char *user;
40    const char *group;
41    int timeout;
42    const char *keypath;
43} options_t;
44
45
46static void usage(void)
47{
48    puts("usage: pvfs2-gencred [-u user] [-t timeout] [-k keyfile]");
49}
50
51static int safe_write(int fd, const void *buf, size_t nbyte)
52{
53    const char *cbuf = (const char *)buf;
54    ssize_t total;
55    ssize_t cnt;
56
57    for (total = 0; total < nbyte; total += cnt)
58    {
59        do cnt = write(fd, cbuf+total, (nbyte - total));
60        while (cnt == -1 && errno == EINTR);
61        if (cnt == -1)
62        {
63            return -1;
64        }
65    }
66
67    return 0;
68}
69
70static int parse_options(int argc, char **argv, options_t *opts)
71{
72    int ch;
73   
74    while ((ch = getopt(argc, argv, "u:g:t:k:")) != -1)
75    {
76        switch(ch)
77        {
78            case 'u':
79                opts->user = optarg;
80                break;
81            case 'g':
82                opts->group = optarg;
83                break;
84            case 't':
85                opts->timeout = strtol(optarg, NULL, 10);
86                if (opts->timeout <= 0)
87                {
88                    fprintf(stderr, "%s: illegal timeout -- %s\n", argv[0],
89                            optarg);
90                    usage();
91                    return EXIT_FAILURE;
92                }
93                break;
94            case 'k':
95                opts->keypath = optarg;
96                break;
97            case '?':
98            default:
99                usage();
100                return EXIT_FAILURE;
101        }
102    }
103   
104    return EXIT_SUCCESS;
105}
106
107static int create_credential(const struct passwd *pwd, const gid_t *groups,
108    int ngroups, PVFS_credential *cred)
109{
110    char hostname[HOST_NAME_MAX+1];
111    char *issuer;
112    int i;
113
114    memset(cred, 0, sizeof(*cred));
115
116    issuer = calloc(PVFS_REQ_LIMIT_ISSUER+1, 1);
117    if (issuer == NULL)
118    {
119        return EXIT_FAILURE;
120    }
121    /* issuer field for clients is prefixed with "C:" */
122    issuer[0] = 'C';
123    issuer[1] = ':';
124    gethostname(hostname, HOST_NAME_MAX);
125    hostname[sizeof(hostname)-1] = '\0';
126    strncpy(issuer+2, hostname, PVFS_REQ_LIMIT_ISSUER-2);
127
128    cred->userid = (PVFS_uid)pwd->pw_uid;
129    cred->num_groups = (uint32_t)ngroups;
130    cred->group_array = calloc(ngroups, sizeof(PVFS_gid));
131    if (cred->group_array == NULL)
132    {
133        free(issuer);
134        return EXIT_FAILURE;
135    }
136    for (i = 0; i < ngroups; i++)
137    {
138        cred->group_array[i] = (PVFS_gid)groups[i];
139    }
140    cred->issuer = issuer;
141
142    return EXIT_SUCCESS;
143}
144
145#ifdef ENABLE_SECURITY
146
147static int sign_credential(PVFS_credential *cred, time_t timeout,
148    const char *keypath)
149{
150    FILE *keyfile;
151    struct stat stats;
152    EVP_PKEY *privkey;
153    const EVP_MD *md;
154    EVP_MD_CTX mdctx;
155    int ret;
156
157    keyfile = fopen(keypath, "rb");
158    if (keyfile == NULL)
159    {
160        perror(keypath);
161        return EXIT_FAILURE;
162    }
163
164    ret = fstat(fileno(keyfile), &stats);
165    if (ret == -1)
166    {
167        perror("stat");
168        fclose(keyfile);
169        return EXIT_FAILURE;
170    }
171    if (stats.st_mode & (S_IROTH | S_IWOTH))
172    {
173        fprintf(stderr, "warning: insecure permissions on private key file "
174                "%s\n", keypath);
175    }
176
177    privkey = PEM_read_PrivateKey(keyfile, NULL, NULL, NULL);
178    if (privkey == NULL)
179    {
180        ERR_print_errors_fp(stderr);
181        fclose(keyfile);
182        return EXIT_FAILURE;
183    }
184
185    fclose(keyfile);
186
187    cred->timeout = (PVFS_time)(time(NULL) + timeout);
188    cred->signature = malloc(EVP_PKEY_size(privkey));
189    if (cred->signature == NULL)
190    {
191        EVP_PKEY_free(privkey);
192        return EXIT_FAILURE;
193    }
194
195    md = EVP_PKEY_type(privkey->type) == EVP_PKEY_DSA ? EVP_dss1() :
196         EVP_sha1();
197    EVP_MD_CTX_init(&mdctx);
198
199    ret = EVP_SignInit_ex(&mdctx, md, NULL);
200    ret &= EVP_SignUpdate(&mdctx, &cred->userid, sizeof(PVFS_uid));
201    ret &= EVP_SignUpdate(&mdctx, &cred->num_groups, sizeof(uint32_t));
202    if (cred->num_groups)
203    {
204        ret &= EVP_SignUpdate(&mdctx, cred->group_array,
205            cred->num_groups * sizeof(PVFS_gid));
206    }
207    if (cred->issuer)
208    {
209        ret &= EVP_SignUpdate(&mdctx, cred->issuer,
210            strlen(cred->issuer) * sizeof(char));
211    }
212    ret &= EVP_SignUpdate(&mdctx, &cred->timeout, sizeof(PVFS_time));
213    if (!ret)
214    {
215        ERR_print_errors_fp(stderr);
216        free(cred->signature);
217        EVP_MD_CTX_cleanup(&mdctx);
218        EVP_PKEY_free(privkey);
219        return EXIT_FAILURE;
220    }
221    ret = EVP_SignFinal(&mdctx, cred->signature, &cred->sig_size, privkey);
222    if (!ret)
223    {
224        ERR_print_errors_fp(stderr);
225        free(cred->signature);
226        EVP_MD_CTX_cleanup(&mdctx);
227        EVP_PKEY_free(privkey);
228        return EXIT_FAILURE;
229    }
230
231    EVP_MD_CTX_cleanup(&mdctx);
232    EVP_PKEY_free(privkey);
233
234    return EXIT_SUCCESS;
235}
236
237#else /* !ENABLE_SECURITY */
238
239static int sign_credential(PVFS_credential *cred, time_t timeout,
240    const char *keypath)
241{
242    cred->timeout = (PVFS_time)(time(NULL) + timeout);
243    cred->sig_size = 0;
244    cred->signature = NULL;
245
246    return 0;
247}
248
249#endif /* ENABLE_SECURITY */
250
251static int write_credential(const PVFS_credential *cred,
252    const struct passwd *pwd)
253{
254    char buf[sizeof(PVFS_credential)+extra_size_PVFS_credential] = { 0 };
255    char *pptr = buf;
256    int ret;
257
258    if (isatty(STDOUT_FILENO))
259    {
260        fputs("error: stdout is a tty\n", stderr);
261        return EXIT_FAILURE;
262    }
263
264    encode_PVFS_credential(&pptr, cred);
265    ret = safe_write(STDOUT_FILENO, buf, sizeof(buf));
266    if (ret == -1)
267    {
268        perror("write");
269        return EXIT_FAILURE;
270    }
271
272    return EXIT_SUCCESS;
273}
274
275int main(int argc, char **argv)
276{
277    options_t opts = { NULL, NULL, 0, NULL };
278    const struct passwd *pwd;
279    const struct group *grp;
280    gid_t groups[PVFS_REQ_LIMIT_GROUPS];
281    int ngroups;
282    PVFS_credential credential;
283    int ret = EXIT_SUCCESS;
284   
285    ret = parse_options(argc, argv, &opts);
286    if (ret != EXIT_SUCCESS)
287    {
288        return ret;
289    }
290   
291#ifdef ENABLE_SECURITY
292    OpenSSL_add_all_algorithms();
293    ERR_load_crypto_strings();
294#endif
295   
296    if (opts.user)
297    {
298        unsigned long val;
299        char *endptr;
300
301        val = strtoul(opts.user, &endptr, 10);
302        if (*endptr == '\0' && *opts.user != '\0')
303        {
304            if (val > PVFS_UID_MAX)
305            {
306                pwd = NULL;
307            }
308            else
309            {
310                pwd = getpwuid((uid_t)val);
311            }
312        }
313        else
314        {
315            pwd = getpwnam(opts.user);
316        }
317    }
318    else
319    {
320        pwd = getpwuid(getuid());
321    }
322    if (pwd == NULL)
323    {
324        if (opts.user)
325        {       
326            fprintf(stderr, "unknown user -- %s\n", opts.user);
327        }
328        return EXIT_FAILURE;
329    }
330
331    if (opts.group)
332    {
333        unsigned long val;
334        char *endptr;
335
336        val = strtoul(opts.group, &endptr, 10);
337        if (*endptr == '\0' && *opts.group != '\0')
338        {
339            if (val > PVFS_GID_MAX)
340            {
341                grp = NULL;
342            }
343            else
344            {
345                grp = getgrgid((gid_t)val);
346            }
347        }
348        else
349        {
350            grp = getgrnam(opts.group);
351        }
352    }
353    else
354    {
355        grp = getgrgid(getgid());
356    }
357    if (grp == NULL)
358    {
359        if (opts.group)
360        {
361            fprintf(stderr, "unknown group -- %s\n", opts.group);
362        }
363        return EXIT_FAILURE;
364    }
365
366    if (getuid() && pwd->pw_uid != getuid())
367    {
368        fprintf(stderr, "error: only %s and root can generate a credential "
369                "for %s\n", pwd->pw_name, pwd->pw_name);
370        return EXIT_FAILURE;
371    }
372
373    if (getuid() && grp->gr_gid != getgid())
374    {
375        fprintf(stderr, "error: cannot generate a credential for group %s: "
376                "Permission denied\n", grp->gr_name);
377        return EXIT_FAILURE;
378    }
379
380#ifdef HAVE_GETGROUPLIST
381
382    ngroups = sizeof(groups)/sizeof(*groups);
383    ret = getgrouplist(pwd->pw_name, grp->gr_gid, groups, &ngroups);
384    if (ret == -1)
385    {
386        fprintf(stderr, "error: unable to get group list for user %s\n",
387                pwd->pw_name);
388        return EXIT_FAILURE;
389    }
390    if (groups[0] != grp->gr_gid)
391    {
392        assert(groups[ngroups-1] == grp->gr_gid);
393        groups[ngroups-1] = groups[0];
394        groups[0] = grp->gr_gid;
395    }
396
397#else /* !HAVE_GETGROUPLIST */
398
399    ngroups = sizeof(groups)/sizeof(*groups);
400    ngroups = getugroups(ngroups, groups, pwd->pw_name, grp->gr_gid);
401    if (ngroups == -1)
402    {
403        fprintf(stderr, "error: unable to get group list for user %s: %s\n",
404                pwd->pw_name, strerror(errno));
405        return EXIT_FAILURE;
406    }
407
408#endif /* HAVE_GETGROUPLIST */
409   
410    ret = create_credential(pwd, groups, ngroups, &credential);
411    if (ret != EXIT_SUCCESS)
412    {
413        free(credential.issuer);
414        free(credential.group_array);
415        return ret;
416    }
417
418    ret = sign_credential(&credential, (opts.timeout ? (time_t)opts.timeout :
419                          DEFAULT_CREDENTIAL_TIMEOUT), (opts.keypath ?
420                          opts.keypath : DEFAULT_CREDENTIAL_KEYPATH));
421    if (ret != EXIT_SUCCESS)
422    {
423        free(credential.issuer);
424        free(credential.group_array);
425        return ret;
426    }
427
428    ret = write_credential(&credential,  pwd);
429    if (ret != EXIT_SUCCESS)
430    {
431        free(credential.issuer);
432        free(credential.group_array);
433        return ret;
434    }
435   
436    free(credential.issuer);
437    free(credential.group_array);
438    return EXIT_SUCCESS;
439}
440
Note: See TracBrowser for help on using the browser.