X-Git-Url: https://git.librecmc.org/?p=oweals%2Fucert.git;a=blobdiff_plain;f=ucert.c;h=6cdc2201fb3933bacfdfbb011102a167571206ee;hp=b932b0998d32e0521e8e07ee251e7dd5e42642e4;hb=46eec66b190470254fc01973f208dc056bcb9780;hpb=c03febfbf8deb75e7126ba42e800318f73423f3a diff --git a/ucert.c b/ucert.c index b932b09..6cdc220 100644 --- a/ucert.c +++ b/ucert.c @@ -47,7 +47,19 @@ static enum { } cmd = CMD_NONE; static bool quiet; +#ifndef UCERT_STRIP_MESSAGES +#define DPRINTF(format, ...) if (!quiet) fprintf(stderr, "%s(%d): " format, __func__, __LINE__, ## __VA_ARGS__) +#else +#define DPRINTF(format, ...) +#endif +/* + * ucert structure + * | BLOB | + * | SIGNATURE | PAYLOAD | + * | |[ BLOBMSG CONTAINER ]| + * | |[[T,i,v,e,f,pubkey ]]| + */ enum cert_attr { CERT_ATTR_SIGNATURE, CERT_ATTR_PAYLOAD, @@ -59,6 +71,15 @@ static const struct blob_attr_info cert_policy[CERT_ATTR_MAX] = { [CERT_ATTR_PAYLOAD] = { .type = BLOB_ATTR_NESTED }, }; +enum cert_cont_attr { + CERT_CT_ATTR_PAYLOAD, + CERT_CT_ATTR_MAX +}; + +static const struct blobmsg_policy cert_cont_policy[CERT_CT_ATTR_MAX] = { + [CERT_CT_ATTR_PAYLOAD] = { .name = "ucert", .type = BLOBMSG_TYPE_TABLE }, +}; + enum cert_payload_attr { CERT_PL_ATTR_CERTTYPE, CERT_PL_ATTR_CERTID, @@ -76,14 +97,21 @@ enum certtype_id { }; static const struct blobmsg_policy cert_payload_policy[CERT_PL_ATTR_MAX] = { - [CERT_PL_ATTR_CERTTYPE] = { .name = "certtype", .type = BLOBMSG_TYPE_INT8 }, - [CERT_PL_ATTR_CERTID] = { .name = "certid", .type = BLOBMSG_TYPE_INT64 }, + [CERT_PL_ATTR_CERTTYPE] = { .name = "certtype", .type = BLOBMSG_TYPE_INT32 }, + [CERT_PL_ATTR_CERTID] = { .name = "certid", .type = BLOBMSG_TYPE_INT32 }, [CERT_PL_ATTR_VALIDFROMTIME] = { .name = "validfrom", .type = BLOBMSG_TYPE_INT64 }, [CERT_PL_ATTR_EXPIRETIME] = { .name = "expiresat", .type = BLOBMSG_TYPE_INT64 }, [CERT_PL_ATTR_PUBKEY] = { .name = "pubkey", .type = BLOBMSG_TYPE_STRING }, [CERT_PL_ATTR_KEY_FINGERPRINT] = { .name = "fingerprint", .type = BLOBMSG_TYPE_STRING }, }; +/* list to store certificate chain at runtime */ +struct cert_object { + struct list_head list; + struct blob_attr *cert[CERT_ATTR_MAX]; +}; + +/* write buffer to file */ static int write_file(const char *filename, void *buf, size_t len, bool append) { FILE *f; size_t outlen; @@ -97,66 +125,293 @@ static int write_file(const char *filename, void *buf, size_t len, bool append) return (outlen == len); } -static int cert_load(const char *certfile, struct blob_attr *certtb[]) { +/* load certfile into list */ +static int cert_load(const char *certfile, struct list_head *chain) { FILE *f; - int ret = 0; + struct blob_attr *certtb[CERT_ATTR_MAX]; + struct blob_attr *bufpt; + struct cert_object *cobj; char filebuf[CERT_BUF_LEN]; - int len; + int ret = 0, pret = 0; + int len, pos = 0; f = fopen(certfile, "r"); if (!f) return 1; len = fread(&filebuf, 1, CERT_BUF_LEN - 1, f); + if (len < 64) + return 1; + ret = ferror(f) || !feof(f); fclose(f); if (ret) return 1; - ret = blob_parse(filebuf, certtb, cert_policy, CERT_ATTR_MAX); - fprintf(stderr, "blob_parse return %d\n", ret); - return (ret != 0); + + bufpt = (struct blob_attr *)filebuf; + do { + pret = blob_parse(bufpt, certtb, cert_policy, CERT_ATTR_MAX); + if (pret <= 0) + /* no attributes found */ + break; + + if (pos + blob_pad_len(bufpt) > len) + /* blob exceeds filebuffer */ + break; + else + pos += blob_pad_len(bufpt); + + if (!certtb[CERT_ATTR_SIGNATURE]) + /* no signature -> drop */ + break; + + cobj = calloc(1, sizeof(*cobj)); + cobj->cert[CERT_ATTR_SIGNATURE] = blob_memdup(certtb[CERT_ATTR_SIGNATURE]); + if (certtb[CERT_ATTR_PAYLOAD]) + cobj->cert[CERT_ATTR_PAYLOAD] = blob_memdup(certtb[CERT_ATTR_PAYLOAD]); + + list_add_tail(&cobj->list, chain); + ret += pret; + /* repeat parsing while there is still enough remaining data in buffer */ + } while(len > pos + sizeof(struct blob_attr) && (bufpt = blob_next(bufpt))); + + return (ret <= 0); } -static int cert_append(const char *certfile, const char *pubkeyfile, const char *sigfile) { - fprintf(stderr, "not implemented\n"); - return 1; +#ifdef UCERT_FULL +/* append signature to certfile */ +static int cert_append(const char *certfile, const char *sigfile) { + FILE *fs; + char filebuf[CERT_BUF_LEN]; + struct blob_buf sigbuf; + int len; + int ret; + + fs = fopen(sigfile, "r"); + if (!fs) + return 1; + + len = fread(&filebuf, 1, CERT_BUF_LEN - 1, fs); + ret = ferror(fs) || !feof(fs) || (len < 64); + fclose(fs); + if (ret) + return 1; + + blob_buf_init(&sigbuf, 0); + blob_put(&sigbuf, CERT_ATTR_SIGNATURE, filebuf, len); + ret = write_file(certfile, sigbuf.head, blob_raw_len(sigbuf.head), true); + blob_buf_free(&sigbuf); + return ret; } +#endif -static int cert_dump(const char *certfile) { - struct blob_attr *certtb[CERT_ATTR_MAX]; +/* verify the signature of a single chain element */ +static int cert_verify_blob(struct blob_attr *cert[CERT_ATTR_MAX], + const char *pubkeyfile, const char *pubkeydir) { int i; + char msgfname[256], sigfname[256]; + int ret; + char tmpdir[] = "/tmp/ucert-XXXXXX"; - if (cert_load(certfile, certtb)) { - fprintf(stderr, "cannot parse cert\n"); - return 1; + if (mkdtemp(tmpdir) == NULL) + return errno; + + snprintf(msgfname, sizeof(msgfname) - 1, "%s/%s", tmpdir, "payload"); + snprintf(sigfname, sizeof(sigfname) - 1, "%s/%s", tmpdir, "payload.sig"); + + for (i = 0; i < CERT_ATTR_MAX; i++) { + struct blob_attr *v = cert[i]; + + if (!v) + break; + + switch(cert_policy[i].type) { + case BLOB_ATTR_BINARY: + write_file(sigfname, blob_data(v), blob_len(v), false); + break; + case BLOB_ATTR_NESTED: + write_file(msgfname, blob_data(v), blob_len(v), false); + break; + } + } + + ret = usign_v(msgfname, pubkeyfile, pubkeydir, sigfname, quiet); + + unlink(msgfname); + unlink(sigfname); + rmdir(tmpdir); + + return ret; +} + +/* verify cert chain (and message) */ +static int chain_verify(const char *msgfile, const char *pubkeyfile, + const char *pubkeydir, struct list_head *chain) { + struct cert_object *cobj; + struct blob_attr *containertb[CERT_CT_ATTR_MAX]; + struct blob_attr *payloadtb[CERT_PL_ATTR_MAX]; + char tmpdir[] = "/tmp/ucert-XXXXXX"; + char chainedpubkey[256] = {0}; + char chainedfp[17] = {0}; + char extsigfile[256] = {0}; + int ret = 1; + int checkmsg = 0; + struct timeval tv; + + if (mkdtemp(tmpdir) == NULL) + return errno; + + if (msgfile) + checkmsg = -1; + + gettimeofday(&tv, NULL); + + list_for_each_entry(cobj, chain, list) { + /* blob has payload, verify that using signature */ + if (cobj->cert[CERT_ATTR_PAYLOAD]) { + uint64_t validfrom; + uint64_t expiresat; + uint32_t certtype; + + ret = cert_verify_blob(cobj->cert, chainedpubkey[0]?chainedpubkey:pubkeyfile, pubkeydir); + if (ret) + goto clean_and_return; + + blobmsg_parse(cert_cont_policy, + ARRAY_SIZE(cert_cont_policy), + containertb, + blob_data(cobj->cert[CERT_ATTR_PAYLOAD]), + blob_len(cobj->cert[CERT_ATTR_PAYLOAD])); + if (!containertb[CERT_CT_ATTR_PAYLOAD]) { + ret = 1; + DPRINTF("no ucert in signed payload\n"); + goto clean_and_return; + } + blobmsg_parse(cert_payload_policy, + ARRAY_SIZE(cert_payload_policy), + payloadtb, + blobmsg_data(containertb[CERT_CT_ATTR_PAYLOAD]), + blobmsg_data_len(containertb[CERT_CT_ATTR_PAYLOAD])); + + if (!payloadtb[CERT_PL_ATTR_CERTTYPE] || + !payloadtb[CERT_PL_ATTR_VALIDFROMTIME] || + !payloadtb[CERT_PL_ATTR_EXPIRETIME] || + !payloadtb[CERT_PL_ATTR_PUBKEY]) { + ret = 1; + DPRINTF("missing mandatory ucert attributes\n"); + goto clean_and_return; + } + certtype = blobmsg_get_u32(payloadtb[CERT_PL_ATTR_CERTTYPE]); + validfrom = blobmsg_get_u64(payloadtb[CERT_PL_ATTR_VALIDFROMTIME]); + expiresat = blobmsg_get_u64(payloadtb[CERT_PL_ATTR_EXPIRETIME]); + + if (certtype != CERTTYPE_AUTH) { + ret = 2; + DPRINTF("wrong certificate type\n"); + goto clean_and_return; + } + + if (tv.tv_sec < validfrom || + tv.tv_sec >= expiresat) { + ret = 3; + DPRINTF("certificate expired\n"); + goto clean_and_return; + } + + snprintf(chainedpubkey, sizeof(chainedpubkey) - 1, "%s/%s", tmpdir, "chained-pubkey"); + write_file(chainedpubkey, + blobmsg_data(payloadtb[CERT_PL_ATTR_PUBKEY]), + blobmsg_data_len(payloadtb[CERT_PL_ATTR_PUBKEY]), + false); + + if (usign_f_pubkey(chainedfp, chainedpubkey)) { + DPRINTF("cannot get fingerprint for chained key\n"); + ret = 2; + goto clean_and_return; + } + if (pubkeydir && _usign_key_is_revoked(chainedfp, pubkeydir)) { + DPRINTF("key %s has been revoked!\n", chainedfp); + ret = 4; + goto clean_and_return; + } + } else { + /* blob doesn't have payload, verify message using signature */ + if (msgfile) { + snprintf(extsigfile, sizeof(extsigfile) - 1, "%s/%s", tmpdir, "ext-sig"); + write_file(extsigfile, + blob_data(cobj->cert[CERT_ATTR_SIGNATURE]), + blob_len(cobj->cert[CERT_ATTR_SIGNATURE]), + false); + checkmsg = ret = usign_v(msgfile, + chainedpubkey[0]?chainedpubkey:pubkeyfile, + pubkeydir, extsigfile, quiet); + unlink(extsigfile); + } else { + DPRINTF("stray trailing signature without anything to verify!\n"); + ret = 1; + }; + } } + if (checkmsg == -1) + DPRINTF("missing signature to verify message!\n"); + +clean_and_return: + if (chainedpubkey[0]) + unlink(chainedpubkey); + rmdir(tmpdir); + return ret | checkmsg; +} + +#ifdef UCERT_FULL +/* dump single chain element to console */ +static void cert_dump_blob(struct blob_attr *cert[CERT_ATTR_MAX]) { + int i; + for (i = 0; i < CERT_ATTR_MAX; i++) { - struct blob_attr *v = certtb[i]; + struct blob_attr *v = cert[i]; if (!v) continue; switch(cert_policy[i].type) { case BLOB_ATTR_BINARY: - fprintf(stdout, "signature: %s\n", blob_data(v)); + fprintf(stdout, "signature:\n---\n%s---\n", (char *) blob_data(v)); break; case BLOB_ATTR_NESTED: - fprintf(stdout, "payload:\n%s\n", blobmsg_format_json(blob_data(v), true)); + fprintf(stdout, "payload:\n---\n%s\n---\n", blobmsg_format_json_indent(blob_data(v), false, 0)); break; } } +} + +/* dump certfile to console */ +static int cert_dump(const char *certfile) { + struct cert_object *cobj; + static LIST_HEAD(certchain); + unsigned int count = 0; + + if (cert_load(certfile, &certchain)) { + DPRINTF("cannot parse cert\n"); + return 1; + } + + list_for_each_entry(cobj, &certchain, list) { + fprintf(stdout, "=== CHAIN ELEMENT %02u ===\n", ++count); + cert_dump_blob(cobj->cert); + } + return 0; } +/* issue an auth certificate for pubkey */ static int cert_issue(const char *certfile, const char *pubkeyfile, const char *seckeyfile) { struct blob_buf certbuf; struct blob_buf payloadbuf; struct timeval tv; - struct stat st; int pklen, siglen; int revoker = 1; - + void *c; FILE *pkf, *sigf; char pkb[512]; char sigb[512]; @@ -164,17 +419,12 @@ static int cert_issue(const char *certfile, const char *pubkeyfile, const char * char pkfp[17]; char tmpdir[] = "/tmp/ucert-XXXXXX"; - if (stat(certfile, &st) == 0) { - fprintf(stderr, "certfile %s exists, won't overwrite.\n", certfile); - return -1; - } - pkf = fopen(pubkeyfile, "r"); if (!pkf) return -1; pklen = fread(pkb, 1, 512, pkf); - pkb[pklen - 1] = '\0'; + pkb[pklen] = '\0'; if (pklen < 32) return -1; @@ -191,7 +441,8 @@ static int cert_issue(const char *certfile, const char *pubkeyfile, const char * while (revoker >= 0) { blob_buf_init(&payloadbuf, 0); - blobmsg_add_u8(&payloadbuf, "certtype", revoker?CERTTYPE_REVOKE:CERTTYPE_AUTH); + c = blobmsg_open_table(&payloadbuf, "ucert"); + blobmsg_add_u32(&payloadbuf, "certtype", revoker?CERTTYPE_REVOKE:CERTTYPE_AUTH); blobmsg_add_u64(&payloadbuf, "validfrom", tv.tv_sec); if (!revoker) { blobmsg_add_u64(&payloadbuf, "expiresat", tv.tv_sec + 60 * 60 * 24 * 365); @@ -200,6 +451,8 @@ static int cert_issue(const char *certfile, const char *pubkeyfile, const char * blobmsg_add_string(&payloadbuf, "fingerprint", pkfp); } + blobmsg_close_table(&payloadbuf, c); + snprintf(fname, sizeof(fname) - 1, "%s/%s", tmpdir, revoker?"revoker":"payload"); write_file(fname, blob_data(payloadbuf.head), blob_len(payloadbuf.head), false); @@ -215,7 +468,7 @@ static int cert_issue(const char *certfile, const char *pubkeyfile, const char * if (siglen < 1) return 1; - sigb[siglen-1] = '\0'; + sigb[siglen] = '\0'; fclose(sigf); unlink(fname); @@ -225,7 +478,10 @@ static int cert_issue(const char *certfile, const char *pubkeyfile, const char * blob_put(&certbuf, CERT_ATTR_SIGNATURE, sigb, siglen); blob_put(&certbuf, CERT_ATTR_PAYLOAD, blob_data(payloadbuf.head), blob_len(payloadbuf.head)); snprintf(fname, sizeof(fname) - 1, "%s%s", certfile, revoker?".revoke":""); - write_file(fname, blob_data(certbuf.head), blob_len(certbuf.head), false); + write_file(fname, certbuf.head, blob_raw_len(certbuf.head), true); + blob_buf_free(&certbuf); + blob_buf_free(&payloadbuf); + revoker--; } @@ -233,97 +489,234 @@ static int cert_issue(const char *certfile, const char *pubkeyfile, const char * return 0; } +#endif + +/* process revoker certificate */ +static int cert_process_revoker(const char *certfile, const char *pubkeydir) { + static LIST_HEAD(certchain); + struct cert_object *cobj; + struct blob_attr *containertb[CERT_CT_ATTR_MAX]; + struct blob_attr *payloadtb[CERT_PL_ATTR_MAX]; + struct stat st; + struct timeval tv; + uint64_t validfrom; + uint32_t certtype; + char *fingerprint; + char rfname[512]; -static int cert_process_revoker(const char *certfile) { - fprintf(stderr, "not implemented\n"); - return 1; + int ret; + + if (cert_load(certfile, &certchain)) { + DPRINTF("cannot parse cert\n"); + return 1; + } + + gettimeofday(&tv, NULL); + + list_for_each_entry(cobj, &certchain, list) { + if (!cobj->cert[CERT_ATTR_PAYLOAD]) + return 2; + + /* blob has payload, verify that using signature */ + ret = cert_verify_blob(cobj->cert, NULL, pubkeydir); + if (ret) + return ret; + + blobmsg_parse(cert_cont_policy, + ARRAY_SIZE(cert_cont_policy), + containertb, + blob_data(cobj->cert[CERT_ATTR_PAYLOAD]), + blob_len(cobj->cert[CERT_ATTR_PAYLOAD])); + if (!containertb[CERT_CT_ATTR_PAYLOAD]) { + DPRINTF("no ucert in signed payload\n"); + return 2; + } + + blobmsg_parse(cert_payload_policy, + ARRAY_SIZE(cert_payload_policy), + payloadtb, + blobmsg_data(containertb[CERT_CT_ATTR_PAYLOAD]), + blobmsg_data_len(containertb[CERT_CT_ATTR_PAYLOAD])); + + if (!payloadtb[CERT_PL_ATTR_CERTTYPE] || + !payloadtb[CERT_PL_ATTR_VALIDFROMTIME] || + !payloadtb[CERT_PL_ATTR_KEY_FINGERPRINT]) { + DPRINTF("missing mandatory ucert attributes\n"); + return 2; + } + + certtype = blobmsg_get_u32(payloadtb[CERT_PL_ATTR_CERTTYPE]); + validfrom = blobmsg_get_u64(payloadtb[CERT_PL_ATTR_VALIDFROMTIME]); + fingerprint = blobmsg_get_string(payloadtb[CERT_PL_ATTR_KEY_FINGERPRINT]); + + if (certtype != CERTTYPE_REVOKE) { + DPRINTF("wrong certificate type\n"); + return 2; + } + + if (tv.tv_sec < validfrom) { + return 3; + } + + snprintf(rfname, sizeof(rfname)-1, "%s/%s", pubkeydir, fingerprint); + /* check if entry in pubkeydir exists */ + if (stat(rfname, &st) == 0) { + if (_usign_key_is_revoked(fingerprint, pubkeydir)) { + DPRINTF("existing revoker deadlink for key %s\n", fingerprint); + continue; + }; + + /* remove any other entry */ + if (unlink(rfname)) + return -1; + } + + ret = symlink(".revoked.", rfname); + if (ret) + return ret; + + DPRINTF("created revoker deadlink for key %s\n", fingerprint); + }; + + return ret; } +/* load and verify certfile (and message) */ static int cert_verify(const char *certfile, const char *pubkeyfile, const char *pubkeydir, const char *msgfile) { - fprintf(stderr, "not implemented\n"); - return 1; + static LIST_HEAD(certchain); + + if (cert_load(certfile, &certchain)) { + DPRINTF("cannot parse cert\n"); + return 1; + } + + return chain_verify(msgfile, pubkeyfile, pubkeydir, &certchain); } +/* output help */ static int usage(const char *cmd) { +#ifndef UCERT_STRIP_MESSAGES fprintf(stderr, "Usage: %s \n" "Commands:\n" - " -A: append (needs -c and -p and/or -x)\n" - " -D: dump\n" +#ifdef UCERT_FULL + " -A: append signature (needs -c and -x)\n" + " -D: dump (needs -c)\n" " -I: issue cert and revoker (needs -c and -p and -s)\n" - " -R: process revoker certificate (needs -c)\n" - " -V: verify (needs -c and -p|-P)\n" +#endif /* UCERT_FULL */ + " -R: process revoker certificate (needs -c and -P)\n" + " -V: verify (needs -c and -p|-P, may have -m)\n" "Options:\n" " -c : certificate file\n" " -m : message file (verify only)\n" " -p : public key file\n" " -P : public key directory (verify only)\n" " -q: quiet (do not print verification result, use return code only)\n" +#ifdef UCERT_FULL " -s : secret key file (issue only)\n" - " -x : signature file\n" + " -x : signature file (append only)\n" +#endif /* UCERT_FULL */ "\n", cmd); +#endif /* UCERT_STRIP_MESSAGES */ return 1; } +/* parse command line options and call functions */ int main(int argc, char *argv[]) { int ch; const char *msgfile = NULL; - const char *sigfile = NULL; const char *pubkeyfile = NULL; const char *pubkeydir = NULL; const char *certfile = NULL; +#ifdef UCERT_FULL + const char *sigfile = NULL; const char *seckeyfile = NULL; +#endif quiet = false; - while ((ch = getopt(argc, argv, "ADIRVc:m:p:P:qs:x:")) != -1) { + while ((ch = getopt(argc, argv, + "RVc:m:p:P:q" +#ifdef UCERT_FULL + "ADIs:x:" +#endif + )) != -1) { switch (ch) { +#ifdef UCERT_FULL case 'A': + if (cmd != CMD_NONE) + return usage(argv[0]); cmd = CMD_APPEND; break; case 'D': + if (cmd != CMD_NONE) + return usage(argv[0]); cmd = CMD_DUMP; break; case 'I': + if (cmd != CMD_NONE) + return usage(argv[0]); cmd = CMD_ISSUE; break; +#endif case 'R': + if (cmd != CMD_NONE) + return usage(argv[0]); cmd = CMD_REVOKE; break; case 'V': + if (cmd != CMD_NONE) + return usage(argv[0]); cmd = CMD_VERIFY; break; case 'c': + if (certfile || cmd == CMD_NONE) + return usage(argv[0]); certfile = optarg; break; case 'm': + if (msgfile || cmd != CMD_VERIFY) + return usage(argv[0]); msgfile = optarg; break; case 'p': + if (pubkeyfile || (cmd != CMD_VERIFY && cmd != CMD_ISSUE) || cmd == CMD_NONE) + return usage(argv[0]); pubkeyfile = optarg; break; case 'P': + if (pubkeydir || (cmd != CMD_VERIFY && cmd != CMD_REVOKE) || cmd == CMD_NONE) + return usage(argv[0]); pubkeydir = optarg; break; - case 's': - seckeyfile = optarg; - break; case 'q': + if (quiet) + return usage(argv[0]); quiet = true; break; +#ifdef UCERT_FULL + case 's': + if (seckeyfile || cmd != CMD_ISSUE || cmd == CMD_NONE) + return usage(argv[0]); + seckeyfile = optarg; + break; case 'x': + if (sigfile || cmd != CMD_APPEND || cmd == CMD_NONE) + return usage(argv[0]); sigfile = optarg; break; +#endif default: return usage(argv[0]); } } switch (cmd) { +#ifdef UCERT_FULL case CMD_APPEND: - if (certfile && (pubkeyfile || sigfile)) - return cert_append(certfile, pubkeyfile, sigfile); + if (certfile && sigfile) + return cert_append(certfile, sigfile); else return usage(argv[0]); case CMD_DUMP: @@ -336,9 +729,10 @@ int main(int argc, char *argv[]) { return cert_issue(certfile, pubkeyfile, seckeyfile); else return usage(argv[0]); +#endif case CMD_REVOKE: - if (certfile) - return cert_process_revoker(certfile); + if (certfile && pubkeydir) + return cert_process_revoker(certfile, pubkeydir); else return usage(argv[0]); case CMD_VERIFY: @@ -346,7 +740,7 @@ int main(int argc, char *argv[]) { return cert_verify(certfile, pubkeyfile, pubkeydir, msgfile); else return usage(argv[0]); - case CMD_NONE: + default: return usage(argv[0]); }