root/branches/cu-security-branch/src/apps/admin/pvfs2-gencred.c @ 8354

Revision 8354, 7.5 KB (checked in by nlmills, 3 years ago)

moved security types into main pvfs2-types.h header

Line 
1/*
2 * Copyright 2010 Clemson University and The University of Chicago.
3 *
4 * See COPYING in top-level directory.
5 */
6
7#include <stdlib.h>
8#include <stdio.h>
9#include <string.h>
10#include <assert.h>
11#include <sys/types.h>
12#include <sys/stat.h>
13#include <fcntl.h>
14#include <errno.h>
15#include <time.h>
16#include <pwd.h>
17#include <grp.h>
18#include <unistd.h>
19
20#include <openssl/err.h>
21#include <openssl/evp.h>
22#include <openssl/pem.h>
23
24#define __PINT_REQPROTO_ENCODE_FUNCS_C
25#include "pvfs2-types.h"
26#include "src/proto/pvfs2-req-proto.h"
27
28
29/* nlmills: TODO: move these somewhere sane */
30#define DEFAULT_CREDENTIAL_TIMEOUT (15*60)
31#define DEFAULT_CREDENTIAL_KEYPATH SYSCONFDIR "/pvfs2credkey.pri"
32
33
34typedef struct {
35    const char *user;
36    int timeout;
37    const char *keypath;
38} options_t;
39
40
41static void usage(void)
42{
43    puts("usage: pvfs2-gencred [-u user] [-t timeout] [-k keyfile]");
44}
45
46static int safe_write(int fd, const void *buf, size_t nbyte)
47{
48    const char *cbuf = (const char *)buf;
49    ssize_t total;
50    ssize_t cnt;
51
52    for (total = 0; total < nbyte; total += cnt)
53    {
54        do cnt = write(fd, cbuf+total, (nbyte - total));
55        while (cnt == -1 && errno == EINTR);
56        if (cnt == -1)
57        {
58            return -1;
59        }
60    }
61
62    return 0;
63}
64
65static int parse_options(int argc, char **argv, options_t *opts)
66{
67    int ch;
68   
69    while ((ch = getopt(argc, argv, "u:t:k:")) != -1)
70    {
71        switch(ch)
72        {
73            case 'u':
74                opts->user = optarg;
75                break;
76            case 't':
77                opts->timeout = strtol(optarg, NULL, 10);
78                if (opts->timeout <= 0)
79                {
80                    fprintf(stderr, "%s: illegal timeout -- %s\n", argv[0],
81                            optarg);
82                    usage();
83                    return EXIT_FAILURE;
84                }
85                break;
86            case 'k':
87                opts->keypath = optarg;
88                break;
89            case '?':
90            default:
91                usage();
92                return EXIT_FAILURE;
93        }
94    }
95   
96    return EXIT_SUCCESS;
97}
98
99static int create_credential(const struct passwd *pwd, const gid_t *groups,
100    int ngroups, PVFS_credential *cred)
101{
102    char hostname[HOST_NAME_MAX+1];
103    char *issuer;
104    int i;
105
106    memset(cred, 0, sizeof(*cred));
107
108    issuer = calloc(PVFS_REQ_LIMIT_ISSUER+1, 1);
109    if (issuer == NULL)
110    {
111        return EXIT_FAILURE;
112    }
113    gethostname(hostname, HOST_NAME_MAX);
114    hostname[sizeof(hostname)-1] = '\0';
115    strncpy(issuer, hostname, PVFS_REQ_LIMIT_ISSUER);
116
117    cred->userid = (PVFS_uid)pwd->pw_uid;
118    cred->num_groups = (uint32_t)ngroups;
119    cred->group_array = calloc(ngroups, sizeof(PVFS_gid));
120    if (cred->group_array == NULL)
121    {
122        free(issuer);
123        return EXIT_FAILURE;
124    }
125    for (i = 0; i < ngroups; i++)
126    {
127        cred->group_array[i] = (PVFS_gid)groups[i];
128    }
129    cred->issuer = issuer;
130
131    return EXIT_SUCCESS;
132}
133
134static int sign_credential(PVFS_credential *cred, time_t timeout,
135    const char *keypath)
136{
137    FILE *keyfile;
138    struct stat stats;
139    EVP_PKEY *privkey;
140    const EVP_MD *md;
141    EVP_MD_CTX mdctx;
142    int ret;
143
144    keyfile = fopen(keypath, "rb");
145    if (keyfile == NULL)
146    {
147        perror(keypath);
148        return EXIT_FAILURE;
149    }
150
151    ret = fstat(fileno(keyfile), &stats);
152    if (ret == -1)
153    {
154        perror("stat");
155        fclose(keyfile);
156        return EXIT_FAILURE;
157    }
158    if (stats.st_mode & (S_IROTH | S_IWOTH))
159    {
160        fprintf(stderr, "warning: insecure permissions on private key file "
161                "%s\n", keypath);
162    }
163
164    privkey = PEM_read_PrivateKey(keyfile, NULL, NULL, NULL);
165    if (privkey == NULL)
166    {
167        ERR_print_errors_fp(stderr);
168        fclose(keyfile);
169        return EXIT_FAILURE;
170    }
171
172    fclose(keyfile);
173
174    cred->timeout = (PVFS_time)(time(NULL) + timeout);
175    cred->signature = malloc(EVP_PKEY_size(privkey));
176    if (cred->signature == NULL)
177    {
178        EVP_PKEY_free(privkey);
179        return EXIT_FAILURE;
180    }
181
182    md = EVP_PKEY_type(privkey->type) == EVP_PKEY_DSA ? EVP_dss1() :
183         EVP_sha1();
184    EVP_MD_CTX_init(&mdctx);
185
186    ret = EVP_SignInit_ex(&mdctx, md, NULL);
187    ret &= EVP_SignUpdate(&mdctx, &cred->userid, sizeof(PVFS_uid));
188    ret &= EVP_SignUpdate(&mdctx, &cred->num_groups, sizeof(uint32_t));
189    if (cred->num_groups)
190    {
191        ret &= EVP_SignUpdate(&mdctx, cred->group_array,
192            cred->num_groups * sizeof(PVFS_gid));
193    }
194    if (cred->issuer)
195    {
196        ret &= EVP_SignUpdate(&mdctx, cred->issuer,
197            strlen(cred->issuer) * sizeof(char));
198    }
199    ret &= EVP_SignUpdate(&mdctx, &cred->timeout, sizeof(PVFS_time));
200    if (!ret)
201    {
202        ERR_print_errors_fp(stderr);
203        free(cred->signature);
204        EVP_MD_CTX_cleanup(&mdctx);
205        EVP_PKEY_free(privkey);
206        return EXIT_FAILURE;
207    }
208    ret = EVP_SignFinal(&mdctx, cred->signature, &cred->sig_size, privkey);
209    if (!ret)
210    {
211        ERR_print_errors_fp(stderr);
212        free(cred->signature);
213        EVP_MD_CTX_cleanup(&mdctx);
214        EVP_PKEY_free(privkey);
215        return EXIT_FAILURE;
216    }
217
218    EVP_MD_CTX_cleanup(&mdctx);
219    EVP_PKEY_free(privkey);
220
221    return EXIT_SUCCESS;
222}
223
224static int write_credential(const PVFS_credential *cred,
225    const struct passwd *pwd)
226{
227    char buf[sizeof(PVFS_credential)+extra_size_PVFS_credential];
228    char *pptr = buf;
229    int ret;
230
231    if (isatty(STDOUT_FILENO))
232    {
233        fputs("error: stdout is a tty\n", stderr);
234        return EXIT_FAILURE;
235    }
236
237    encode_PVFS_credential(&pptr, cred);
238    ret = safe_write(STDOUT_FILENO, buf, sizeof(buf));
239    if (ret == -1)
240    {
241        perror("write");
242        return EXIT_FAILURE;
243    }
244
245    return EXIT_SUCCESS;
246}
247
248int main(int argc, char **argv)
249{
250    options_t opts = { NULL, 0, NULL };
251    const struct passwd *pwd;
252    gid_t groups[PVFS_REQ_LIMIT_GROUPS];
253    int ngroups;
254    PVFS_credential credential;
255    int ret;
256   
257    ret = parse_options(argc, argv, &opts);
258    if (ret != EXIT_SUCCESS)
259    {
260        return ret;
261    }
262   
263    OpenSSL_add_all_algorithms();
264    ERR_load_crypto_strings();
265   
266    pwd = opts.user ? getpwnam(opts.user) : getpwuid(getuid());
267    if (pwd == NULL)
268    {
269        fprintf(stderr, "unknown user -- %s\n", opts.user);
270        return EXIT_FAILURE;
271    }
272   
273    if (getuid() && pwd->pw_uid != getuid())
274    {
275        fprintf(stderr, "error: only %s and root can generate a credential "
276                "for %s\n", pwd->pw_name, pwd->pw_name);
277        return EXIT_FAILURE;
278    }
279   
280    /* nlmills: TODO: fall back to getugroups */
281   
282    ngroups = sizeof(groups)/sizeof(*groups);
283    ret = getgrouplist(pwd->pw_name, pwd->pw_gid, groups, &ngroups);
284    if (ret == -1)
285    {
286        fprintf(stderr, "error: unable to get group list for user %s\n",
287                pwd->pw_name);
288        return EXIT_FAILURE;
289    }
290    if (groups[0] != pwd->pw_gid)
291    {
292        assert(groups[ngroups-1] == pwd->pw_gid);
293        groups[ngroups-1] = groups[0];
294        groups[0] = pwd->pw_gid;
295    }
296   
297    ret = create_credential(pwd, groups, ngroups, &credential);
298    if (ret != EXIT_SUCCESS)
299    {
300        return ret;
301    }
302
303    ret = sign_credential(&credential, (opts.timeout ? (time_t)opts.timeout :
304                          DEFAULT_CREDENTIAL_TIMEOUT), (opts.keypath ?
305                          opts.keypath : DEFAULT_CREDENTIAL_KEYPATH));
306    if (ret != EXIT_SUCCESS)
307    {
308        return ret;
309    }
310
311    ret = write_credential(&credential,  pwd);
312    if (ret != EXIT_SUCCESS)
313    {
314        return ret;
315    }
316   
317    return EXIT_SUCCESS;
318}
319
Note: See TracBrowser for help on using the browser.