Index: generic/tclOpts.h ================================================================== --- generic/tclOpts.h +++ generic/tclOpts.h @@ -1,67 +1,28 @@ /* - * Copyright (C) 1997-2000 Matt Newman - * - * Stylized option processing - requires consistent - * external vars: opt, idx, objc, objv + * Convenient option processing */ #ifndef _TCL_OPTS_H #define _TCL_OPTS_H -#define OPTFLAG(option, var, val) \ - if (strcmp(opt, (option)) == 0) { \ - var = val; \ - continue; \ - } - -#define OPT_PROLOG(option) \ - if (strcmp(opt, (option)) == 0) { \ - if (++idx >= objc) { \ - Tcl_AppendResult(interp, \ - "no argument given for ", \ - (option), " option", \ - (char *) NULL); \ - return TCL_ERROR; \ - } - -#define OPT_POSTLOG() \ - continue; \ - } - -#define OPTOBJ(option, var) \ - OPT_PROLOG(option) \ - var = objv[idx]; \ - OPT_POSTLOG() - -#define OPTSTR(option, var) \ - OPT_PROLOG(option) \ - var = Tcl_GetStringFromObj(objv[idx], (Tcl_Size *)NULL);\ - OPT_POSTLOG() - -#define OPTINT(option, var) \ - OPT_PROLOG(option) \ - if (Tcl_GetIntFromObj(interp, objv[idx], \ - &(var)) != TCL_OK) { \ - return TCL_ERROR; \ - } \ - OPT_POSTLOG() - -#define OPTBOOL(option, var) \ - OPT_PROLOG(option) \ - if (Tcl_GetBooleanFromObj(interp, objv[idx],\ - &(var)) != TCL_OK) { \ - return TCL_ERROR; \ - } \ - OPT_POSTLOG() - -#define OPTBYTE(option, var, lvar) \ - OPT_PROLOG(option) \ - var = Tcl_GetByteArrayFromObj(objv[idx], &(lvar));\ - OPT_POSTLOG() - -#define OPTBAD(type, list) \ - Tcl_AppendResult(interp, "bad ", (type), \ - " \"", opt, "\": must be ", \ - (list), (char *) NULL) +#define GET_OPT_BOOL(objPtr, varPtr) \ + if (Tcl_GetBooleanFromObj(interp, objPtr, varPtr) != TCL_OK) { \ + return TCL_ERROR; \ + } + +#define GET_OPT_INT(objPtr, varPtr) \ + if (Tcl_GetIntFromObj(interp, objPtr, varPtr) != TCL_OK) { \ + return TCL_ERROR; \ + } + +#define GET_OPT_STRING(objPtr, var, lenPtr) \ + if ((var = Tcl_GetStringFromObj(objPtr, lenPtr)) == NULL) { \ + return TCL_ERROR; \ + } \ + +#define GET_OPT_BYTE_ARRAY(objPtr, var, lenPtr) \ + if ((var = Tcl_GetByteArrayFromObj(objPtr, lenPtr)) == NULL) { \ + return TCL_ERROR; \ + } \ #endif /* _TCL_OPTS_H */ Index: generic/tls.c ================================================================== --- generic/tls.c +++ generic/tls.c @@ -1010,10 +1010,24 @@ Tcl_SetObjResult(interp, Tcl_NewIntObj(ret)); return(TCL_OK); clientData = clientData; } +static const char *command_opts [] = { + "-alpn", "-cadir", "-cafile", "-cert", "-certfile", "-cipher", "-ciphers", "-ciphersuites", + "-command", "-dhparams", "-key", "-keyfile", "-model", "-password", "-post_handshake", + "-request", "-require", "-security_level", "-server", "-servername", "-session_id", "-ssl2", + "-ssl3", "-tls1", "-tls1.1", "-tls1.2", "-tls1.3", "-validatecommand", "-vcmd", NULL}; + +enum _command_opts { + _opt_alpn, _opt_cadir, _opt_cafile, _opt_cert, _opt_certfile, _opt_cipher, _opt_ciphers, + _opt_ciphersuite, _opt_cmd, _opt_dhparams, _opt_key, _opt_keyfile, _opt_model, _opt_password, + _opt_handshake, _opt_request, _opt_require, _opt_security_level, _opt_server, _opt_servername, + _opt_session_id, _opt_ssl2, _opt_ssl3, _opt_tls1, _opt_tls11, _opt_tls12, _opt_tls13, + _opt_validate, _opt_vcmd +}; + /* *------------------------------------------------------------------- * * ImportObjCmd -- * @@ -1032,15 +1046,15 @@ static int ImportObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_Channel chan; /* The channel to set a mode on. */ State *statePtr; /* client state for ssl socket */ SSL_CTX *ctx = NULL; - Tcl_Obj *script = NULL; - Tcl_Obj *password = NULL; + Tcl_Obj *cmdObj = NULL; + Tcl_Obj *passwdObj = NULL; Tcl_Obj *vcmd = NULL; Tcl_DString upperChannelTranslation, upperChannelBlocking, upperChannelEncoding, upperChannelEOFChar; - int idx, len; + int idx, len, fn; int flags = TLS_TCL_INIT; int server = 0; /* is connection incoming or outgoing? */ char *keyfile = NULL; char *certfile = NULL; unsigned char *key = NULL; @@ -1053,11 +1067,12 @@ char *CAdir = NULL; char *DHparams = NULL; char *model = NULL; char *servername = NULL; /* hostname for Server Name Indication */ const unsigned char *session_id = NULL; - Tcl_Obj *alpn = NULL; + int sess_len = 0; + Tcl_Obj *alpnObj = NULL; int ssl2 = 0, ssl3 = 0; int tls1 = 1, tls1_1 = 1, tls1_2 = 1, tls1_3 = 1; int proto = 0, level = -1; int verify = 0, require = 0, request = 1, post_handshake = 0; @@ -1090,49 +1105,107 @@ /* Make sure to operate on the topmost channel */ chan = Tcl_GetTopChannel(chan); for (idx = 2; idx < objc; idx++) { - char *opt = Tcl_GetStringFromObj(objv[idx], NULL); + if (Tcl_GetIndexFromObj(interp, objv[idx], command_opts, "option", 0, &fn) != TCL_OK) { + return TCL_ERROR; + } - if (opt[0] != '-') + /* Validate arg has value */ + if (++idx >= objc) { + Tcl_AppendResult(interp, "No value for option \"", command_opts[fn], "\"", (char *) NULL); + return TCL_ERROR; + } + + switch(fn) { + case _opt_alpn: + alpnObj = objv[idx]; + break; + case _opt_cadir: + GET_OPT_STRING(objv[idx], CAdir, NULL); + break; + case _opt_cafile: + GET_OPT_STRING(objv[idx], CAfile, NULL); + break; + case _opt_cert: + GET_OPT_BYTE_ARRAY(objv[idx], cert, &cert_len); + break; + case _opt_certfile: + GET_OPT_STRING(objv[idx], certfile, NULL); + break; + case _opt_cipher: + case _opt_ciphers: + GET_OPT_STRING(objv[idx], ciphers, NULL); + break; + case _opt_ciphersuite: + GET_OPT_STRING(objv[idx], ciphersuites, NULL); + break; + case _opt_cmd: + cmdObj = objv[idx]; + break; + case _opt_dhparams: + GET_OPT_STRING(objv[idx], DHparams, NULL); + break; + case _opt_key: + GET_OPT_BYTE_ARRAY(objv[idx], key, &key_len); + break; + case _opt_keyfile: + GET_OPT_STRING(objv[idx], keyfile, NULL); + break; + case _opt_model: + GET_OPT_STRING(objv[idx], model, NULL); + break; + case _opt_password: + passwdObj = objv[idx]; + break; + case _opt_handshake: + GET_OPT_BOOL(objv[idx], &post_handshake); + break; + case _opt_request: + GET_OPT_BOOL(objv[idx], &request); + break; + case _opt_require: + GET_OPT_BOOL(objv[idx], &require); + break; + case _opt_security_level: + GET_OPT_INT(objv[idx], &level); + break; + case _opt_server: + GET_OPT_BOOL(objv[idx], &server); + break; + case _opt_servername: + GET_OPT_STRING(objv[idx], servername, NULL); + break; + case _opt_session_id: + GET_OPT_BYTE_ARRAY(objv[idx], session_id, &sess_len); + break; + case _opt_ssl2: + GET_OPT_INT(objv[idx], &ssl2); + break; + case _opt_ssl3: + GET_OPT_INT(objv[idx], &ssl3); + break; + case _opt_tls1: + GET_OPT_INT(objv[idx], &tls1); + break; + case _opt_tls11: + GET_OPT_INT(objv[idx], &tls1_1); + break; + case _opt_tls12: + GET_OPT_INT(objv[idx], &tls1_2); + break; + case _opt_tls13: + GET_OPT_INT(objv[idx], &tls1_3); + break; + case _opt_validate: + case _opt_vcmd: + vcmd = objv[idx]; break; - - OPTOBJ("-alpn", alpn); - OPTSTR("-cadir", CAdir); - OPTSTR("-cafile", CAfile); - OPTBYTE("-cert", cert, cert_len); - OPTSTR("-certfile", certfile); - OPTSTR("-cipher", ciphers); - OPTSTR("-ciphers", ciphers); - OPTSTR("-ciphersuites", ciphersuites); - OPTOBJ("-command", script); - OPTSTR("-dhparams", DHparams); - OPTBYTE("-key", key, key_len); - OPTSTR("-keyfile", keyfile); - OPTSTR("-model", model); - OPTOBJ("-password", password); - OPTBOOL("-post_handshake", post_handshake); - OPTBOOL("-request", request); - OPTBOOL("-require", require); - OPTINT("-security_level", level); - OPTBOOL("-server", server); - OPTSTR("-servername", servername); - OPTSTR("-session_id", session_id); - OPTBOOL("-ssl2", ssl2); - OPTBOOL("-ssl3", ssl3); - OPTBOOL("-tls1", tls1); - OPTBOOL("-tls1.1", tls1_1); - OPTBOOL("-tls1.2", tls1_2); - OPTBOOL("-tls1.3", tls1_3); - OPTOBJ("-validatecommand", vcmd); - OPTOBJ("-vcmd", vcmd); - - OPTBAD("option", "-alpn, -cadir, -cafile, -cert, -certfile, -cipher, -ciphersuites, -command, -dhparams, -key, -keyfile, -model, -password, -post_handshake, -request, -require, -security_level, -server, -servername, -session_id, -ssl2, -ssl3, -tls1, -tls1.1, -tls1.2, -tls1.3, or -validatecommand"); - - return TCL_ERROR; - } + } + } + if (request) verify |= SSL_VERIFY_CLIENT_ONCE | SSL_VERIFY_PEER; if (request && require) verify |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; if (request && post_handshake) verify |= SSL_VERIFY_POST_HANDSHAKE; if (verify == 0) verify = SSL_VERIFY_NONE; @@ -1162,23 +1235,23 @@ statePtr->interp = interp; statePtr->vflags = verify; statePtr->err = ""; /* allocate script */ - if (script) { - (void) Tcl_GetStringFromObj(script, &len); + if (cmdObj != NULL) { + (void) Tcl_GetStringFromObj(cmdObj, &len); if (len) { - statePtr->callback = script; + statePtr->callback = cmdObj; Tcl_IncrRefCount(statePtr->callback); } } /* allocate password */ - if (password) { - (void) Tcl_GetStringFromObj(password, &len); + if (passwdObj != NULL) { + (void) Tcl_GetStringFromObj(passwdObj, &len); if (len) { - statePtr->password = password; + statePtr->password = passwdObj; Tcl_IncrRefCount(statePtr->password); } } /* allocate validate command */ @@ -1285,30 +1358,30 @@ return TCL_ERROR; } } /* Resume session id */ - if (session_id && strlen(session_id) <= SSL_MAX_SID_CTX_LENGTH) { + if (session_id && sess_len <= SSL_MAX_SID_CTX_LENGTH) { /* SSL_set_session() */ - if (!SSL_SESSION_set1_id_context(SSL_get_session(statePtr->ssl), session_id, (unsigned int) strlen(session_id))) { + if (!SSL_SESSION_set1_id_context(SSL_get_session(statePtr->ssl), session_id, (unsigned int) sess_len)) { Tcl_AppendResult(interp, "Resume session id ", session_id, " failed", (char *) NULL); Tcl_SetErrorCode(interp, "TLS", "IMPORT", "SESSION", "FAILED", (char *) NULL); Tls_Free((char *) statePtr); return TCL_ERROR; } } /* Enable Application-Layer Protocol Negotiation. Examples are: http/1.0, http/1.1, h2, h3, ftp, imap, pop3, xmpp-client, xmpp-server, mqtt, irc, etc. */ - if (alpn) { + if (alpnObj != NULL) { /* Convert a TCL list into a protocol-list in wire-format */ unsigned char *protos, *p; unsigned int protos_len = 0; int i, len, cnt; Tcl_Obj **list; - if (Tcl_ListObjGetElements(interp, alpn, &cnt, &list) != TCL_OK) { + if (Tcl_ListObjGetElements(interp, alpnObj, &cnt, &list) != TCL_OK) { Tls_Free((char *) statePtr); return TCL_ERROR; } /* Determine the memory required for the protocol-list */ Index: generic/tlsDigest.c ================================================================== --- generic/tlsDigest.c +++ generic/tlsDigest.c @@ -818,10 +818,11 @@ dprintf("Called"); /* Validate args */ if (channel == (const char *) NULL) { + Tcl_AppendResult(interp, "No channel", (char *) NULL); return TCL_ERROR; } /* Get channel Id */ chan = Tcl_GetChannel(interp, channel, &mode); @@ -1171,10 +1172,19 @@ return res; } /*******************************************************************/ +static const char *command_opts [] = { "-bin", "-binary", "-hex", "-hexadecimal", + "-chan", "-channel", "-cipher", "-command", "-data", "-digest", "-file", "-filename", + "-hash", "-key", "-mac", NULL}; + +enum _command_opts { + _opt_bin, _opt_binary, _opt_hex, _opt_hexadecimal, _opt_chan, _opt_channel, _opt_cipher, + _opt_command, _opt_data, _opt_digest, _opt_file, _opt_filename, _opt_hash, _opt_key, _opt_mac +}; + /* *------------------------------------------------------------------- * * DigestMain -- * @@ -1188,11 +1198,11 @@ * Sets result to message digest or error message * *------------------------------------------------------------------- */ static int DigestMain(int type, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - int idx, start = 1, format = HEX_FORMAT, res = TCL_OK; + int start = 1, format = HEX_FORMAT, res = TCL_OK, fn; Tcl_Obj *cipherObj = NULL, *cmdObj = NULL, *dataObj = NULL, *digestObj = NULL; Tcl_Obj *fileObj = NULL, *keyObj = NULL, *macObj = NULL; const char *channel = NULL, *opt; dprintf("Called"); @@ -1207,60 +1217,113 @@ } /* Special case of first arg is digest, cipher, or mac */ opt = Tcl_GetStringFromObj(objv[start], NULL); if (opt[0] != '-') { - if (type == TYPE_MD || type == TYPE_HMAC) { - digestObj = objv[start]; - start++; - } else if (type == TYPE_CMAC) { - cipherObj = objv[start]; - start++; - } else if (type == TYPE_MAC) { - macObj = objv[start]; - start++; + switch(type) { + case TYPE_MD: + case TYPE_HMAC: + digestObj = objv[start++]; + break; + case TYPE_CMAC: + cipherObj = objv[start++]; + break; + case TYPE_MAC: + macObj = objv[start++]; + break; } } /* Get options */ - for (idx = start; idx < objc; idx++) { - opt = Tcl_GetStringFromObj(objv[idx], NULL); - - if (opt[0] != '-') { - break; - } - - OPTFLAG("-bin", format, BIN_FORMAT); - OPTFLAG("-binary", format, BIN_FORMAT); - OPTFLAG("-hex", format, HEX_FORMAT); - OPTFLAG("-hexadecimal", format, HEX_FORMAT); - OPTSTR("-chan", channel); - OPTSTR("-channel", channel); - OPTOBJ("-cipher", cipherObj); - OPTOBJ("-command", cmdObj); - OPTOBJ("-data", dataObj); - OPTOBJ("-digest", digestObj); - OPTOBJ("-file", fileObj); - OPTOBJ("-filename", fileObj); - OPTOBJ("-hash", digestObj); - OPTOBJ("-key", keyObj); - OPTOBJ("-mac", macObj); - - OPTBAD("option", "-bin, -channel, -cipher, -command, -data, -digest, -file, -filename, -hex, -key, or -mac"); - return TCL_ERROR; - } - - /* If only 1 arg left, it's the data */ - if (idx < objc && dataObj == NULL) { - dataObj = objv[idx]; + for (int idx = start; idx < objc; idx++) { + /* Special case for when last arg is data */ + if (idx == objc - 1) { + opt = Tcl_GetStringFromObj(objv[idx], NULL); + if (opt[0] != '-' && dataObj == NULL) { + dataObj = objv[idx]; + break; + } + } + + /* Get option */ + if (Tcl_GetIndexFromObj(interp, objv[idx], command_opts, "option", 0, &fn) != TCL_OK) { + return TCL_ERROR; + } + + /* Validate arg has value */ + if (fn > _opt_hexadecimal) { + if (++idx >= objc) { + Tcl_AppendResult(interp, "No value for option \"", command_opts[fn], "\"", (char *) NULL); + return TCL_ERROR; + } + } + + switch(fn) { + case _opt_bin: + case _opt_binary: + format = BIN_FORMAT; + break; + case _opt_hex: + case _opt_hexadecimal: + format = HEX_FORMAT; + break; + case _opt_chan: + case _opt_channel: + GET_OPT_STRING(objv[idx], channel, NULL); + break; + case _opt_cipher: + cipherObj = objv[idx]; + break; + case _opt_command: + cmdObj = objv[idx]; + break; + case _opt_data: + dataObj = objv[idx]; + break; + case _opt_digest: + case _opt_hash: + digestObj = objv[idx]; + break; + case _opt_file: + case _opt_filename: + fileObj = objv[idx]; + break; + case _opt_key: + keyObj = objv[idx]; + break; + case _opt_mac: + macObj = objv[idx]; + break; + } } /* Check types */ - if (type == TYPE_MD && cipherObj != NULL) { - type = TYPE_CMAC; - } else if (type == TYPE_MD && keyObj != NULL) { - type = TYPE_HMAC; + if (type == TYPE_MD) { + if (macObj != NULL) { + type = TYPE_MAC; + } else if (cipherObj != NULL) { + type = TYPE_CMAC; + } else if (keyObj != NULL) { + type = TYPE_HMAC; + } + } + + if (type == TYPE_MAC) { + if (macObj != NULL) { + char *macName = Tcl_GetStringFromObj(macObj, NULL); + if (strcmp(macName,"cmac") == 0) { + type = TYPE_CMAC; + } else if (strcmp(macName,"hmac") == 0) { + type = TYPE_HMAC; + } else { + Tcl_AppendResult(interp, "Invalid MAC \"", macName, "\"", NULL); + return TCL_ERROR; + } + } else { + Tcl_AppendResult(interp, "No MAC specified", NULL); + return TCL_ERROR; + } } /* Calc digest on file, stacked channel, using instance command, or data blob */ if (fileObj != NULL) { res = DigestFileHandler(interp, fileObj, digestObj, cipherObj, format | type, keyObj, macObj); @@ -1321,40 +1384,46 @@ * Side effects: * Sets result to message digest or error message * *------------------------------------------------------------------- */ - #define VALIDATE_ARGC(objc, objv) { \ - if (objc != 2) { \ - Tcl_WrongNumArgs(interp, 1, objv, "data"); \ - return TCL_ERROR; \ - } \ + int TemplateCmd(Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], char *digestName, int format) { + Tcl_Obj *dataObj, *digestObj; + int res; + + if (objc == 2) { + dataObj = objv[1]; + } else { + Tcl_WrongNumArgs(interp, 1, objv, "data"); + return TCL_ERROR; + } + + digestObj = Tcl_NewStringObj(digestName, -1); + Tcl_IncrRefCount(digestObj); + res = DigestDataHandler(interp, dataObj, digestObj, NULL, format, NULL, NULL); + Tcl_DecrRefCount(digestObj); + return res; } int MD4ObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - VALIDATE_ARGC(objc, objv); - return DigestDataHandler(interp, objv[1], EVP_md4(), NULL, HEX_FORMAT | TYPE_MD, NULL, NULL); + return TemplateCmd(interp, objc, objv, "md4", HEX_FORMAT | TYPE_MD); } int MD5ObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - VALIDATE_ARGC(objc, objv); - return DigestDataHandler(interp, objv[1], EVP_md5(), NULL, HEX_FORMAT | TYPE_MD, NULL, NULL); + return TemplateCmd(interp, objc, objv, "md5", HEX_FORMAT | TYPE_MD); } int SHA1ObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - VALIDATE_ARGC(objc, objv); - return DigestDataHandler(interp, objv[1], EVP_sha1(), NULL, HEX_FORMAT | TYPE_MD, NULL, NULL); + return TemplateCmd(interp, objc, objv, "sha1", HEX_FORMAT | TYPE_MD); } int SHA256ObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - VALIDATE_ARGC(objc, objv); - return DigestDataHandler(interp, objv[1], EVP_sha256(), NULL, HEX_FORMAT | TYPE_MD, NULL, NULL); + return TemplateCmd(interp, objc, objv, "sha256", HEX_FORMAT | TYPE_MD); } int SHA512ObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - VALIDATE_ARGC(objc, objv); - return DigestDataHandler(interp, objv[1], EVP_sha512(), NULL, HEX_FORMAT | TYPE_MD, NULL, NULL); + return TemplateCmd(interp, objc, objv, "sha512", HEX_FORMAT | TYPE_MD); } /* *------------------------------------------------------------------- * Index: generic/tlsEncrypt.c ================================================================== --- generic/tlsEncrypt.c +++ generic/tlsEncrypt.c @@ -784,10 +784,11 @@ dprintf("Called"); /* Validate args */ if (channel == (const char *) NULL) { + Tcl_AppendResult(interp, "No channel", (char *) NULL); return TCL_ERROR; } /* Get channel Id */ chan = Tcl_GetChannel(interp, channel, &mode); @@ -1193,10 +1194,19 @@ return res; } /*******************************************************************/ +static const char *command_opts [] = { + "-chan", "-channel", "-cipher", "-command", "-data", "-digest", "-infile", "-filename", + "-outfile", "-hash", "-iv", "-key", "-mac", NULL}; + +enum _command_opts { + _opt_chan, _opt_channel, _opt_cipher, _opt_command, _opt_data, _opt_digest, _opt_infile, + _opt_filename, _opt_outfile, _opt_hash, _opt_iv, _opt_key, _opt_mac +}; + /* *------------------------------------------------------------------- * * EncryptMain -- * @@ -1212,60 +1222,91 @@ */ static int EncryptMain(int type, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_Obj *cipherObj = NULL, *cmdObj = NULL, *dataObj = NULL, *digestObj = NULL; Tcl_Obj *inFileObj = NULL, *outFileObj = NULL, *keyObj = NULL, *ivObj = NULL, *macObj = NULL; const char *channel = NULL, *opt; - int idx, res, start = 1; + int res, start = 1, fn; dprintf("Called"); /* Clear interp result */ Tcl_ResetResult(interp); /* Validate arg count */ if (objc < 3 || objc > 12) { - Tcl_WrongNumArgs(interp, 1, objv, "-cipher name ?-digest name? -key key ?-iv string? ?-mac name? [-channel chan | -command cmdName | -infile filename -outfile filename | -data data]"); + Tcl_WrongNumArgs(interp, 1, objv, "?-cipher? name ?-digest name? -key key ?-iv string? ?-mac name? [-channel chan | -command cmdName | -infile filename -outfile filename | ?-data? data]"); return TCL_ERROR; } /* Special case of first arg is cipher */ opt = Tcl_GetStringFromObj(objv[start], NULL); if (opt[0] != '-') { - if (type == TYPE_ENCRYPT || type == TYPE_DECRYPT) { - cipherObj = objv[start]; - start++; + switch(type) { + case TYPE_ENCRYPT: + case TYPE_DECRYPT: + cipherObj = objv[start++]; + break; } } /* Get options */ - for (idx = start; idx < objc; idx++) { - opt = Tcl_GetStringFromObj(objv[idx], NULL); - - if (opt[0] != '-') { - break; - } - - OPTSTR("-chan", channel); - OPTSTR("-channel", channel); - OPTOBJ("-cipher", cipherObj); - OPTOBJ("-command", cmdObj); - OPTOBJ("-data", dataObj); - OPTOBJ("-digest", digestObj); - OPTOBJ("-hash", digestObj); - OPTOBJ("-infile", inFileObj); - OPTOBJ("-outfile", outFileObj); - OPTOBJ("-key", keyObj); - OPTOBJ("-iv", ivObj); - OPTOBJ("-mac", macObj); - - OPTBAD("option", "-chan, -channel, -cipher, -command, -data, -digest, -infile, -key, -iv, -mac, -outfile"); + for (int idx = start; idx < objc; idx++) { + /* Special case for when last arg is data */ + if (idx == objc - 1) { + opt = Tcl_GetStringFromObj(objv[idx], NULL); + if (opt[0] != '-' && dataObj == NULL) { + dataObj = objv[idx]; + break; + } + } + + /* Get option */ + if (Tcl_GetIndexFromObj(interp, objv[idx], command_opts, "option", 0, &fn) != TCL_OK) { + return TCL_ERROR; + } + + /* Validate arg has value */ + if (++idx >= objc) { + Tcl_AppendResult(interp, "No value for option \"", command_opts[fn], "\"", (char *) NULL); return TCL_ERROR; } - /* If only 1 arg left, it's the data */ - if (idx < objc && dataObj == NULL) { - dataObj = objv[idx]; + switch(fn) { + case _opt_chan: + case _opt_channel: + GET_OPT_STRING(objv[idx], channel, NULL); + break; + case _opt_cipher: + cipherObj = objv[idx]; + break; + case _opt_command: + cmdObj = objv[idx]; + break; + case _opt_data: + dataObj = objv[idx]; + break; + case _opt_digest: + case _opt_hash: + digestObj = objv[idx]; + break; + case _opt_infile: + case _opt_filename: + inFileObj = objv[idx]; + break; + case _opt_outfile: + outFileObj = objv[idx]; + break; + case _opt_iv: + ivObj = objv[idx]; + break; + case _opt_key: + keyObj = objv[idx]; + break; + case _opt_mac: + macObj = objv[idx]; + break; + } } /* Check for required options */ if (cipherObj == NULL) { Tcl_AppendResult(interp, "No cipher", NULL); @@ -1282,11 +1323,11 @@ } else if (cmdObj != NULL) { res = EncryptCommandHandler(interp, type, cmdObj, cipherObj, digestObj, keyObj, ivObj); } else if (dataObj != NULL) { res = EncryptDataHandler(interp, type, dataObj, cipherObj, digestObj, keyObj, ivObj); } else { - Tcl_AppendResult(interp, "No operation specified: Use -channel, -command, -data, -infile, or -outfile option", NULL); + Tcl_AppendResult(interp, "No operation specified: Use -channel, -command, -data, or -infile option", NULL); res = TCL_ERROR; } return res; } Index: generic/tlsKey.c ================================================================== --- generic/tlsKey.c +++ generic/tlsKey.c @@ -11,10 +11,17 @@ #include "tclOpts.h" #include /*******************************************************************/ +static const char *command_opts [] = { + "-cipher", "-digest", "-hash", "-iterations", "-password", "-salt", "-size", NULL}; + +enum _command_opts { + _opt_cipher, _opt_digest, _opt_hash, _opt_iter, _opt_password, _opt_salt, _opt_size +}; + /* *------------------------------------------------------------------- * * DeriveKey -- * @@ -28,18 +35,19 @@ * Sets result to a list of key and iv values, or an error message * *------------------------------------------------------------------- */ static int DeriveKey(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - int key_len = 0, md_len = 0, pass_len = 0, salt_len = 0; + int key_len = 0, md_len = 0, pass_len = 0, salt_len = 0, fn; int iklen, ivlen, iter = PKCS5_DEFAULT_ITER; unsigned char *passwd = NULL, *salt = NULL; - Tcl_Obj *cipherObj = NULL, *digestObj = NULL, *passwdObj = NULL, *saltObj = NULL, *resultObj; + Tcl_Obj *resultObj; const EVP_MD *md = NULL; const EVP_CIPHER *cipher = NULL; int max = EVP_MAX_KEY_LENGTH + EVP_MAX_IV_LENGTH, size = max; unsigned char tmpkeyiv[EVP_MAX_KEY_LENGTH + EVP_MAX_IV_LENGTH]; + char *cipherName = NULL, *digestName = NULL; dprintf("Called"); /* Clear errors */ Tcl_ResetResult(interp); @@ -54,45 +62,66 @@ /* Init buffers */ memset(tmpkeyiv, 0, EVP_MAX_KEY_LENGTH + EVP_MAX_IV_LENGTH); /* Get options */ for (int idx = 1; idx < objc; idx++) { - char *opt = Tcl_GetStringFromObj(objv[idx], NULL); - - if (opt[0] != '-') { - break; - } - - OPTOBJ("-cipher", cipherObj); - OPTOBJ("-digest", digestObj); - OPTOBJ("-hash", digestObj); - OPTINT("-iterations", iter); - OPTOBJ("-password", passwdObj); - OPTOBJ("-salt", saltObj); - OPTINT("-size", size); - - OPTBAD("option", "-cipher, -digest, -iterations, -password, -salt, or -size option"); - return TCL_ERROR; - } - - /* Validate options */ - if (cipherObj != NULL) { - char *name = Tcl_GetByteArrayFromObj(cipherObj, NULL); - cipher = EVP_get_cipherbyname(name); - } - if (digestObj != NULL) { - char *name = Tcl_GetStringFromObj(digestObj, &md_len); - md = EVP_get_digestbyname(name); - } else { - Tcl_AppendResult(interp, "No digest specified", NULL); - return TCL_ERROR; - } - if (passwdObj != NULL) { - passwd = Tcl_GetByteArrayFromObj(passwdObj, &pass_len); - } - if (saltObj != NULL) { - salt = Tcl_GetByteArrayFromObj(saltObj, &salt_len); + /* Get option */ + if (Tcl_GetIndexFromObj(interp, objv[idx], command_opts, "option", 0, &fn) != TCL_OK) { + return TCL_ERROR; + } + + /* Validate arg has value */ + if (++idx >= objc) { + Tcl_AppendResult(interp, "No value for option \"", command_opts[fn], "\"", (char *) NULL); + return TCL_ERROR; + } + + switch(fn) { + case _opt_cipher: + GET_OPT_STRING(objv[idx], cipherName, NULL); + break; + case _opt_digest: + case _opt_hash: + GET_OPT_STRING(objv[idx], digestName, NULL); + break; + case _opt_iter: + GET_OPT_INT(objv[idx], &iter); + break; + case _opt_password: + GET_OPT_BYTE_ARRAY(objv[idx], passwd, &pass_len); + break; + case _opt_salt: + GET_OPT_BYTE_ARRAY(objv[idx], salt, &salt_len); + break; + case _opt_size: + GET_OPT_INT(objv[idx], &size); + break; + } + } + + /* Validate options */ + if (cipherName != NULL) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L + cipher = EVP_get_cipherbyname(cipherName); +#else + cipher = EVP_CIPHER_fetch(NULL, cipherName, NULL); +#endif + if (cipher == NULL) { + Tcl_AppendResult(interp, "Invalid cipher: \"", cipherName, "\"", NULL); + return TCL_ERROR; + } + } + if (digestName != NULL) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L + md = EVP_get_digestbyname(digestName); +#else + md = EVP_MD_fetch(NULL, digestName, NULL); +#endif + if (md == NULL) { + Tcl_AppendResult(interp, "Invalid digest: \"", digestName, "\"", NULL); + return TCL_ERROR; + } } if (iter < 1) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("Invalid iterations count %d: must be > 0", iter)); return TCL_ERROR; } Index: tests/digest.csv ================================================================== --- tests/digest.csv +++ tests/digest.csv @@ -79,11 +79,11 @@ MD Errors,Too few args,,,::tls::md,,,"wrong # args: should be ""::tls::md ?-bin|-hex? ?-cipher name? ?-digest name? ?-key key? ?-mac name? [-channel chan | -command cmdName | -file filename | ?-data? data]""",,,1 MD Errors,Too many args,,,::tls::md too many command line args to pass the test without an error or failing,,,"wrong # args: should be ""::tls::md ?-bin|-hex? ?-cipher name? ?-digest name? ?-key key? ?-mac name? [-channel chan | -command cmdName | -file filename | ?-data? data]""",,,1 MD Errors,Invalid digest,,,::tls::md bogus data,,,"Invalid digest ""bogus""",,,1 MD Errors,Invalid digest Arg,,,::tls::md -digest bogus -data data,,,"Invalid digest ""bogus""",,,1 MD Errors,No digest,,,::tls::md -hex -data value,,,No digest specified,,,1 -MD Errors,Invalid option,,,::tls::md -digest sha256 -bogus value,,,"bad option ""-bogus"": must be -bin, -channel, -cipher, -command, -data, -digest, -file, -filename, -hex, -key, or -mac",,,1 +MD Errors,Invalid option,,,::tls::md -digest sha256 -bogus value,,,"bad option ""-bogus"": must be -bin, -binary, -hex, -hexadecimal, -chan, -channel, -cipher, -command, -data, -digest, -file, -filename, -hash, -key, or -mac",,,1 MD Errors,Invalid file,,,::tls::md -digest sha256 -file bogus,,,"couldn't open ""bogus"": no such file or directory",,,1 MD Errors,Invalid channel,,,::tls::md -digest sha256 -channel bogus,,,"can not find channel named ""bogus""",,,1 MD Errors,No operation,,,::tls::md -digest sha256 -bin,,,"No operation specified: Use -channel, -command, -data, or -file option",,,1 ,,,,,,,,,, ,,,,,,,,,, Index: tests/digest.test ================================================================== --- tests/digest.test +++ tests/digest.test @@ -249,11 +249,11 @@ ::tls::md -hex -data value } -result {No digest specified} -returnCodes {1} test MD_Errors-8.6 {Invalid option} -body { ::tls::md -digest sha256 -bogus value - } -result {bad option "-bogus": must be -bin, -channel, -cipher, -command, -data, -digest, -file, -filename, -hex, -key, or -mac} -returnCodes {1} + } -result {bad option "-bogus": must be -bin, -binary, -hex, -hexadecimal, -chan, -channel, -cipher, -command, -data, -digest, -file, -filename, -hash, -key, or -mac} -returnCodes {1} test MD_Errors-8.7 {Invalid file} -body { ::tls::md -digest sha256 -file bogus } -result {couldn't open "bogus": no such file or directory} -returnCodes {1} Index: tests/info.csv ================================================================== --- tests/info.csv +++ tests/info.csv @@ -76,12 +76,12 @@ command,# Test show version,,,,,,,,, Version,All,,,::tls::version,,glob,*,,, Version,OpenSSL,OpenSSL,,::tls::version,,glob,OpenSSL*,,, ,,,,,,,,,, command,# Error Cases,,,,,,,,, -Error Cases,Cipher Too few args,,,::tls::cipher,,,"wrong # args: should be ""::tls::cipher name""",,,1 -Error Cases,Cipher Too many args,,,::tls::cipher too many args,,,"wrong # args: should be ""::tls::cipher name""",,,1 +Error Cases,Cipher Too few args,,,::tls::cipher,,,"wrong # args: should be ""::tls::cipher ?name?""",,,1 +Error Cases,Cipher Too many args,,,::tls::cipher too many args,,,"wrong # args: should be ""::tls::cipher ?name?""",,,1 Error Cases,Digests Too many args,,,::tls::digests too many args,,,"wrong # args: should be ""::tls::digests ?name?""",,,1 Error Cases,MACs Too many args,,,::tls::macs too many args,,,"wrong # args: should be ""::tls::macs ?name?""",,,1 Error Cases,Pkeys Too many args,,,::tls::pkeys too many args,,,"wrong # args: should be ""::tls::pkeys ?name?""",,,1 Error Cases,Protocols Too many args,,,::tls::protocols too many args,,,"wrong # args: should be ""::tls::protocols""",,,1 Error Cases,Version Too many args,,,::tls::version too many args,,,"wrong # args: should be ""::tls::version""",,,1 Index: tests/info.test ================================================================== --- tests/info.test +++ tests/info.test @@ -212,15 +212,15 @@ # Error Cases test Error_Cases-13.1 {Cipher Too few args} -body { ::tls::cipher - } -result {wrong # args: should be "::tls::cipher name"} -returnCodes {1} + } -result {wrong # args: should be "::tls::cipher ?name?"} -returnCodes {1} test Error_Cases-13.2 {Cipher Too many args} -body { ::tls::cipher too many args - } -result {wrong # args: should be "::tls::cipher name"} -returnCodes {1} + } -result {wrong # args: should be "::tls::cipher ?name?"} -returnCodes {1} test Error_Cases-13.3 {Digests Too many args} -body { ::tls::digests too many args } -result {wrong # args: should be "::tls::digests ?name?"} -returnCodes {1}