source: creddy/attribute.c @ 3ba7805

abac0-leakabac0-meicompt_changesgec13mei-idmei-rt0-nmei_rt0mei_rt2mei_rt2_fix_1meiyap-rt1meiyap1rt2tvf-new-xml
Last change on this file since 3ba7805 was 3ba7805, checked in by Ted Faber <faber@…>, 14 years ago

Whoops, no getline on FreeBSD < 8. Over to fgets.

  • Property mode set to 100644
File size: 7.4 KB
Line 
1#define GNU_SOURCE
2#include <credentials/keys/private_key.h>
3
4#include "creddy.h"
5
6#include <termios.h>
7
8#define ROLE_SEPARATOR " <- "
9#define SHA1_LENGTH 40
10/* Size of password memory allocation */
11#define PWLEN 128
12
13/* Callback configuration */
14struct cb_opts {
15    bool use_prompt;    /* Print a prompt to stderr */
16    bool use_echo;      /* If true, turn off input echo on stdin */
17    unsigned int tries; /* Number of attempts allowed */
18    char prompt[20];    /* The prompt to display if use_echo is true */
19};
20
21chunk_t passphrase_callback(void *user, int try) {
22    /* Get a password from stdin and return it as a chunk_t.  If too many tries
23     * have occurred or there is any other problem, return an empty chunk_t,
24     * which libstrongswan takes as giving up.  The chunk is alloated here
25     * (inside getline), and presumably freed by libstrongswan. User points to
26     * a cb_opts struct, which affects this routine in the obvious ways.
27     */
28    /* Configuration options */
29    struct cb_opts *opts = (struct cb_opts *) user;
30    chunk_t rv = chunk_empty;   /* Return value, starts empty */
31
32    if (try -1 < opts->tries ) {
33        struct termios t;   /* Terminal settings */
34        size_t len = 0;     /* Length of string from getline */
35        tcflag_t orig = 0;  /* Holds the original local flags (echo in here) */
36
37        if (!opts->use_echo) {
38            /* Use tc{get,set}attr to turn echo off and restore the intial
39             * echo settings */
40            if (!tcgetattr(0, &t)) { 
41                orig = t.c_lflag;
42
43                t.c_lflag &= ~ECHO;
44                if ( tcsetattr(0, TCSANOW, &t) ) { 
45                    perror("Cannot turn off echo"); 
46                    return rv;
47                }
48            }
49            else {
50                perror("Cannot turn get attributes to off echo"); 
51                return rv;
52            }
53        }
54        if (opts->use_prompt) printf("%s", opts->prompt);
55
56        /* Because rv.ptr starts as NULL, getline allocates memory.  The size
57         * of the allocation returns in rv.len and the size of the string
58         * (including newline and NUL) is in len.  */
59        if ((rv.ptr = (u_char *) malloc(rv.len = PWLEN))) {
60            if ( fgets(rv.ptr, rv.len, stdin) ) {
61                /* Readjust the chunk_t's len field to the size of the string
62                 * w/o the newline or NUL */
63                /* would prefer strnlen, but no such luck in FBSD7 or earlier*/
64                size_t len = strlen(rv.ptr);
65
66                if (rv.ptr[len-2] == '\n') rv.len = len-2;
67                else rv.len = len -1;
68            }
69            else {
70                /* Read failed.  Deallocate and clear rv */
71                free(rv.ptr);
72                rv = chunk_empty;
73            }
74        }
75        else {
76            /* Failed malloc.  Restore rv to empty and return it */
77            perror("malloc");
78            rv = chunk_empty;
79            return rv;
80        }
81
82        if (!opts->use_echo ) {
83            /* Pop echo beck to its original setting. */
84            t.c_lflag = orig;
85
86            if ( tcsetattr(0, TCSANOW, &t) ) 
87                perror("Cannot restore echo setting?"); 
88
89            if (opts->use_prompt) printf("\n");
90        }
91    }
92    else fprintf(stderr, "Too many tries (%d)", try-1);
93    return rv;
94}
95
96
97void attribute_main(options_t *opts) {
98    char *subject_id = NULL;
99    struct cb_opts c_opts = { 1, 0, 3, "Key password:" };
100
101    if (
102        opts->issuer == NULL ||
103        opts->key == NULL ||
104        opts->role == NULL ||
105        opts->out == NULL
106    )
107        usage(opts);
108
109    // make sure we have exactly one of --subject-cert / --subject-id
110    if (opts->subject_cert == NULL && opts->subject_id == NULL)
111        usage(opts);
112    if (opts->subject_cert && opts->subject_id) {
113        printf("Need exactly one of --subject-cert / --subject-id");
114        usage(opts);
115    }
116
117    // if we have an ID, make sure it's SHA1 and lowercase
118    if (opts->subject_id) {
119        int i;
120        subject_id = opts->subject_id;
121
122        // hex chars (also convert to lowercase)
123        for (i = 0; subject_id[i]; ++i) {
124            subject_id[i] = tolower(subject_id[i]);
125            if (!isxdigit(subject_id[i])) {
126                printf("Invalid subject ID: must be SHA1\n");
127                usage(opts);
128            }
129        }
130
131        // correct length
132        if (i != SHA1_LENGTH) {
133            printf("Invalid subject ID: must be SHA1 (too long)\n");
134            usage(opts);
135        }
136    }
137
138    if (!clean_name(opts->role)) {
139        printf("bad role name\n");
140        usage(opts);
141    }
142
143    // verify the subject role name if present
144    if (opts->subject_role) {
145        char *role = opts->subject_role;
146        char *start[3];
147        int name_parts = 0, i;
148
149        start[name_parts++] = role;
150
151        for (i = 0; role[i] != '\0'; ++i)
152            if (role[i] == '.') {
153                if (name_parts == 3) {
154                    printf("bad subject role name (too many dots)\n");
155                    usage(opts);
156                }
157                start[name_parts++] = &role[i+1];
158                role[i] = 0;
159            }
160
161        for (i = 0; i < name_parts; ++i)
162            if (!clean_name(start[i])) {
163                printf("bad subject role name\n");
164                usage(opts);
165            }
166
167        for (i = 1; i < name_parts; ++i)
168            *(start[i]-1) = '.'; // replace the dot
169    }
170
171    if (opts->validity < 0) {
172        printf("Validity must be >= 1 day\n");
173        usage(opts);
174    }
175
176    chunk_t pw = chunk_empty;
177    // load signer key
178    private_key_t *key = lib->creds->create(lib->creds,
179        CRED_PRIVATE_KEY, KEY_RSA,
180        BUILD_FROM_FILE, opts->key,
181        /* Ask for password if the key's encrypted */
182        BUILD_PASSPHRASE_CALLBACK, passphrase_callback, &c_opts, 
183        BUILD_END
184    );
185    if (key == NULL)
186        errx(1, "can't open private key file %s", opts->key);
187
188    // get the keyids
189    certificate_t *issuer = cert_from_file(opts->issuer);
190    char *issuer_id = cert_keyid(issuer);
191
192    // subject keyid from cert if it wasn't passed in
193    if (opts->subject_cert) {
194        certificate_t *subject = cert_from_file(opts->subject_cert);
195        subject_id = cert_keyid(subject);
196        DESTROY_IF(subject);
197    }
198
199    // build the role encoding
200    int role_encoding_len = 1; // for nul terminator
201    role_encoding_len += strlen(issuer_id) + 1 + strlen(opts->role);
202    role_encoding_len += sizeof(ROLE_SEPARATOR) - 1;
203    role_encoding_len += strlen(subject_id);
204    if (opts->subject_role)
205        role_encoding_len += 1 + strlen(opts->subject_role);
206
207    char *role_encoding = xmalloc(role_encoding_len);
208    role_encoding[0] = '\0';
209
210    sprintf(role_encoding, "%s.%s" ROLE_SEPARATOR "%s", issuer_id, opts->role, subject_id);
211    if (opts->subject_role) {
212        strcat(role_encoding, ".");
213        strcat(role_encoding, opts->subject_role);
214    }
215
216    free(issuer_id);
217
218    // if opts->subject_id, the memory will be freed in main
219    if (opts->subject_cert)
220        free(subject_id);
221
222    // create attribute cert
223    time_t not_before = time(NULL);
224    time_t not_after = not_before + opts->validity * 3600 * 60 * 60;
225    chunk_t serial = generate_serial();
226
227    certificate_t *attr_cert = lib->creds->create(lib->creds,
228        CRED_CERTIFICATE, CERT_X509_AC,
229        BUILD_CERT, issuer,
230        BUILD_NOT_BEFORE_TIME, not_before,
231        BUILD_NOT_AFTER_TIME, not_after,
232        BUILD_SERIAL, serial,
233        BUILD_IETF_GROUP_ATTR, role_encoding,
234        BUILD_SIGNING_CERT, issuer,
235        BUILD_SIGNING_KEY, key,
236        BUILD_END
237    );
238    if (attr_cert == NULL)
239        errx(1, "Couldn't build attribute cert");
240
241    // write to file
242    chunk_t encoding = attr_cert->get_encoding(attr_cert);
243
244    FILE *out = fopen(opts->out, "w");
245    if (out == NULL)
246        err(1, "Can't open attribute cert output file %s", opts->out);
247    fwrite(encoding.ptr, encoding.len, 1, out);
248    fclose(out);
249
250    free(role_encoding);
251    free(serial.ptr);
252    DESTROY_IF(attr_cert);
253    DESTROY_IF(issuer);
254    DESTROY_IF(key);
255}
Note: See TracBrowser for help on using the repository browser.