source: creddy/attribute.c @ 01044ac

abac0-leakabac0-meicompt_changesgec13mei-idmei-rt0-nmei_rt0mei_rt2mei_rt2_fix_1meiyap-rt1meiyap1rt2tvf-new-xml
Last change on this file since 01044ac was 01044ac, checked in by T-Fab <faber@…>, 14 years ago

Add callback to provide password for encrypted keys. It's a pretty flexible functon that can be configured for more uses.

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