From 76af3716fe56e728b2cad2df2bec7bb4a5b1c8df Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Wed, 27 Jun 2012 08:42:01 +0000 Subject: oauth re-added git-svn-id: http://svn.miranda-ng.org/main/trunk@650 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/Twitter/oauth/oauth.c | 921 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 921 insertions(+) create mode 100644 protocols/Twitter/oauth/oauth.c (limited to 'protocols/Twitter/oauth/oauth.c') diff --git a/protocols/Twitter/oauth/oauth.c b/protocols/Twitter/oauth/oauth.c new file mode 100644 index 0000000000..0f205572dd --- /dev/null +++ b/protocols/Twitter/oauth/oauth.c @@ -0,0 +1,921 @@ +/* + * OAuth string functions in POSIX-C. + * + * Copyright 2007-2011 Robin Gareus + * + * The base64 functions are by Jan-Henrik Haukeland, + * and un/escape_url() was inspired by libcurl's curl_escape under ISC-license + * many thanks to Daniel Stenberg . + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +#if HAVE_CONFIG_H +# include +#endif + +#define WIPE_MEMORY ///< overwrite sensitve data before free()ing it. + +#include +#include +#include +#include +#include +#include +#include // isxdigit + +#include "xmalloc.h" +#include "oauth.h" + +#ifndef WIN32 // getpid() on POSIX systems +#include +#include +#else +#define snprintf _snprintf +#define strncasecmp strnicmp +#endif + +/** + * Base64 encode one byte + */ +char oauth_b64_encode(unsigned char u) { + if(u < 26) return 'A'+u; + if(u < 52) return 'a'+(u-26); + if(u < 62) return '0'+(u-52); + if(u == 62) return '+'; + return '/'; +} + +/** + * Decode a single base64 character. + */ +unsigned char oauth_b64_decode(char c) { + if(c >= 'A' && c <= 'Z') return(c - 'A'); + if(c >= 'a' && c <= 'z') return(c - 'a' + 26); + if(c >= '0' && c <= '9') return(c - '0' + 52); + if(c == '+') return 62; + return 63; +} + +/** + * Return TRUE if 'c' is a valid base64 character, otherwise FALSE + */ +int oauth_b64_is_base64(char c) { + if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || (c == '+') || + (c == '/') || (c == '=')) { + return 1; + } + return 0; +} + +/** + * Base64 encode and return size data in 'src'. The caller must free the + * returned string. + * + * @param size The size of the data in src + * @param src The data to be base64 encode + * @return encoded string otherwise NULL + */ +char *oauth_encode_base64(int size, const unsigned char *src) { + int i; + char *out, *p; + + if(!src) return NULL; + if(!size) size= strlen((char *)src); + out= (char*) xcalloc(sizeof(char), size*4/3+4); + p= out; + + for(i=0; i>2; + b5= ((b1&0x3)<<4)|(b2>>4); + b6= ((b2&0xf)<<2)|(b3>>6); + b7= b3&0x3f; + + *p++= oauth_b64_encode(b4); + *p++= oauth_b64_encode(b5); + + if(i+1>4) ); + + if(c3 != '=') *p++=(((b2&0xf)<<4)|(b3>>2) ); + if(c4 != '=') *p++=(((b3&0x3)<<6)|b4 ); + } + free(buf); + dest[p-dest]='\0'; + return(p-dest); + } + return 0; +} + +/** + * Escape 'string' according to RFC3986 and + * http://oauth.net/core/1.0/#encoding_parameters. + * + * @param string The data to be encoded + * @return encoded string otherwise NULL + * The caller must free the returned string. + */ +char *oauth_url_escape(const char *string) { + size_t alloc, newlen; + char *ns = NULL, *testing_ptr = NULL; + unsigned char in; + size_t strindex=0; + size_t length; + + if (!string) return xstrdup(""); + + alloc = strlen(string)+1; + newlen = alloc; + + ns = (char*) xmalloc(alloc); + + length = alloc-1; + while(length--) { + in = *string; + + switch(in){ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'o': + case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': case 'G': case 'H': case 'I': case 'J': + case 'K': case 'L': case 'M': case 'N': case 'O': + case 'P': case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': + case '_': case '~': case '.': case '-': + ns[strindex++]=in; + break; + default: + newlen += 2; /* this'll become a %XX */ + if(newlen > alloc) { + alloc *= 2; + testing_ptr = (char*) xrealloc(ns, alloc); + ns = testing_ptr; + } + snprintf(&ns[strindex], 4, "%%%02X", in); + strindex+=3; + break; + } + string++; + } + ns[strindex]=0; + return ns; +} + +#ifndef ISXDIGIT +# define ISXDIGIT(x) (isxdigit((int) ((unsigned char)x))) +#endif + +/** + * Parse RFC3986 encoded 'string' back to unescaped version. + * + * @param string The data to be unescaped + * @param olen unless NULL the length of the returned string is stored there. + * @return decoded string or NULL + * The caller must free the returned string. + */ +char *oauth_url_unescape(const char *string, size_t *olen) { + size_t alloc, strindex=0; + char *ns = NULL; + unsigned char in; + long hex; + + if (!string) return NULL; + alloc = strlen(string)+1; + ns = (char*) xmalloc(alloc); + + while(--alloc > 0) { + in = *string; + if(('%' == in) && ISXDIGIT(string[1]) && ISXDIGIT(string[2])) { + char hexstr[3]; // '%XX' + hexstr[0] = string[1]; + hexstr[1] = string[2]; + hexstr[2] = 0; + hex = strtol(hexstr, NULL, 16); + in = (unsigned char)hex; /* hex is always < 256 */ + string+=2; + alloc-=2; + } + ns[strindex++] = in; + string++; + } + ns[strindex]=0; + if(olen) *olen = strindex; + return ns; +} + +/** + * returns plaintext signature for the given key. + * + * the returned string needs to be freed by the caller + * + * @param m message to be signed + * @param k key used for signing + * @return signature string + */ +char *oauth_sign_plaintext (const char *m, const char *k) { + return(oauth_url_escape(k)); +} + +/** + * encode strings and concatenate with '&' separator. + * The number of strings to be concatenated must be + * given as first argument. + * all arguments thereafter must be of type (char *) + * + * @param len the number of arguments to follow this parameter + * @param ... string to escape and added (may be NULL) + * + * @return pointer to memory holding the concatenated + * strings - needs to be free(d) by the caller. or NULL + * in case we ran out of memory. + */ +char *oauth_catenc(int len, ...) { + va_list va; + int i; + char *rv = (char*) xmalloc(sizeof(char)); + *rv='\0'; + va_start(va, len); + for(i=0;i0)?1:0); + if(rv) len+=strlen(rv); + rv=(char*) xrealloc(rv,len*sizeof(char)); + + if(i>0) strcat(rv, "&"); + strcat(rv, enc); + free(enc); + } + va_end(va); + return(rv); +} + +/** + * splits the given url into a parameter array. + * (see \ref oauth_serialize_url and \ref oauth_serialize_url_parameters for the reverse) + * + * NOTE: Request-parameters-values may include an ampersand character. + * However if unescaped this function will use them as parameter delimiter. + * If you need to make such a request, this function since version 0.3.5 allows + * to use the ASCII SOH (0x01) character as alias for '&' (0x26). + * (the motivation is convenience: SOH is /untypeable/ and much more + * unlikely to appear than '&' - If you plan to sign fancy URLs you + * should not split a query-string, but rather provide the parameter array + * directly to \ref oauth_serialize_url) + * + * @param url the url or query-string to parse. + * @param argv pointer to a (char *) array where the results are stored. + * The array is re-allocated to match the number of parameters and each + * parameter-string is allocated with strdup. - The memory needs to be freed + * by the caller. + * @param qesc use query parameter escape (vs post-param-escape) - if set + * to 1 all '+' are treated as spaces ' ' + * + * @return number of parameter(s) in array. + */ +int oauth_split_post_paramters(const char *url, char ***argv, short qesc) { + int argc=0; + char *token, *tmp, *t1; + if (!argv) return 0; + if (!url) return 0; + t1=xstrdup(url); + + // '+' represents a space, in a URL query string + while ((qesc&1) && (tmp=strchr(t1,'+'))) *tmp=' '; + + tmp=t1; + while((token=strtok(tmp,"&?"))) { + if(!strncasecmp("oauth_signature=",token,16)) continue; + (*argv)=(char**) xrealloc(*argv,sizeof(char*)*(argc+1)); + while (!(qesc&2) && (tmp=strchr(token,'\001'))) *tmp='&'; + if (argc>0 || (qesc&4)) + (*argv)[argc]=oauth_url_unescape(token, NULL); + else + (*argv)[argc]=xstrdup(token); + if (argc==0 && strstr(token, ":/")) { + // HTTP does not allow empty absolute paths, so the URL + // 'http://example.com' is equivalent to 'http://example.com/' and should + // be treated as such for the purposes of OAuth signing (rfc2616, section 3.2.1) + // see http://groups.google.com/group/oauth/browse_thread/thread/c44b6f061bfd98c?hl=en + char *slash=strstr(token, ":/"); + while (slash && *(++slash) == '/') ; // skip slashes eg /xxx:[\/]*/ +#if 0 + // skip possibly unescaped slashes in the userinfo - they're not allowed by RFC2396 but have been seen. + // the hostname/IP may only contain alphanumeric characters - so we're safe there. + if (slash && strchr(slash,'@')) slash=strchr(slash,'@'); +#endif + if (slash && !strchr(slash,'/')) { +#ifdef DEBUG_OAUTH + fprintf(stderr, "\nliboauth: added trailing slash to URL: '%s'\n\n", token); +#endif + free((*argv)[argc]); + (*argv)[argc]= (char*) xmalloc(sizeof(char)*(2+strlen(token))); + strcpy((*argv)[argc],token); + strcat((*argv)[argc],"/"); + } + } + if (argc==0 && (tmp=strstr((*argv)[argc],":80/"))) { + memmove(tmp, tmp+3, strlen(tmp+2)); + } + tmp=NULL; + argc++; + } + + free(t1); + return argc; +} + +int oauth_split_url_parameters(const char *url, char ***argv) { + return oauth_split_post_paramters(url, argv, 1); +} + +/** + * build a url query string from an array. + * + * @param argc the total number of elements in the array + * @param start element in the array at which to start concatenating. + * @param argv parameter-array to concatenate. + * @return url string needs to be freed by the caller. + * + */ +char *oauth_serialize_url (int argc, int start, char **argv) { + return oauth_serialize_url_sep( argc, start, argv, "&", 0); +} + +/** + * encode query parameters from an array. + * + * @param argc the total number of elements in the array + * @param start element in the array at which to start concatenating. + * @param argv parameter-array to concatenate. + * @param sep separator for parameters (usually "&") + * @param mod - bitwise modifiers: + * 1: skip all values that start with "oauth_" + * 2: skip all values that don't start with "oauth_" + * 4: add double quotation marks around values (use with sep=", " to generate HTTP Authorization header). + * @return url string needs to be freed by the caller. + */ +char *oauth_serialize_url_sep (int argc, int start, char **argv, char *sep, int mod) { + char *tmp, *t1; + int i; + int first=1; + int seplen=strlen(sep); + char *query = (char*) xmalloc(sizeof(char)); + *query='\0'; + for(i=start; i< argc; i++) { + int len = 0; + if ((mod&1)==1 && (strncmp(argv[i],"oauth_",6) == 0 || strncmp(argv[i],"x_oauth_",8) == 0) ) continue; + if ((mod&2)==2 && (strncmp(argv[i],"oauth_",6) != 0 && strncmp(argv[i],"x_oauth_",8) != 0) && i!=0) continue; + + if (query) len+=strlen(query); + + if (i==start && i==0 && strstr(argv[i], ":/")) { + tmp=xstrdup(argv[i]); +#if 1 // encode white-space in the base-url + while ((t1=strchr(tmp,' '))) { +# if 0 + *t1='+'; +# else + size_t off = t1-tmp; + char *t2 = (char*) xmalloc(sizeof(char)*(3+strlen(tmp))); + strcpy(t2, tmp); + strcpy(t2+off+2, tmp+off); + *(t2+off)='%'; *(t2+off+1)='2'; *(t2+off+2)='0'; + free(tmp); + tmp=t2; +# endif +#endif + } + len+=strlen(tmp); + } else if(!(t1=strchr(argv[i], '='))) { + // see http://oauth.net/core/1.0/#anchor14 + // escape parameter names and arguments but not the '=' + tmp=xstrdup(argv[i]); + tmp=(char*) xrealloc(tmp,(strlen(tmp)+2)*sizeof(char)); + strcat(tmp,"="); + len+=strlen(tmp); + } else { + *t1=0; + tmp = oauth_url_escape(argv[i]); + *t1='='; + t1 = oauth_url_escape((t1+1)); + tmp=(char*) xrealloc(tmp,(strlen(tmp)+strlen(t1)+2+(mod&4?2:0))*sizeof(char)); + strcat(tmp,"="); + if (mod&4) strcat(tmp,"\""); + strcat(tmp,t1); + if (mod&4) strcat(tmp,"\""); + free(t1); + len+=strlen(tmp); + } + len+=seplen+1; + query=(char*) xrealloc(query,len*sizeof(char)); + strcat(query, ((i==start||first)?"":sep)); + first=0; + strcat(query, tmp); + if (i==start && i==0 && strstr(tmp, ":/")) { + strcat(query, "?"); + first=1; + } + free(tmp); + } + return (query); +} + +/** + * build a query parameter string from an array. + * + * This function is a shortcut for \ref oauth_serialize_url (argc, 1, argv). + * It strips the leading host/path, which is usually the first + * element when using oauth_split_url_parameters on an URL. + * + * @param argc the total number of elements in the array + * @param argv parameter-array to concatenate. + * @return url string needs to be freed by the caller. + */ +char *oauth_serialize_url_parameters (int argc, char **argv) { + return oauth_serialize_url(argc, 1, argv); +} + +/** + * generate a random string between 15 and 32 chars length + * and return a pointer to it. The value needs to be freed by the + * caller + * + * @return zero terminated random string. + */ +#if !defined HAVE_OPENSSL_HMAC_H && !defined USE_NSS +/* pre liboauth-0.7.2 and possible future versions that don't use OpenSSL or NSS */ +char *oauth_gen_nonce() { + char *nc; + static int rndinit = 1; + const char *chars = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789_"; + unsigned int max = strlen( chars ); + int i, len; + + if(rndinit) {srand(time(NULL) +#ifndef WIN32 // quick windows check. + * getpid() +#endif + ); rndinit=0;} // seed random number generator - FIXME: we can do better ;) + + len=15+floor(rand()*16.0/(double)RAND_MAX); + nc = (char*) xmalloc((len+1)*sizeof(char)); + for(i=0;i +# define MY_RAND RAND_bytes +# define MY_SRAND ; +#endif +char *oauth_gen_nonce() { + char *nc; + unsigned char buf; + const char *chars = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789_"; + unsigned int max = strlen(chars); + int i, len; + + MY_SRAND + MY_RAND(&buf, 1); + len=15+(((short)buf)&0x0f); + nc = (char*) xmalloc((len+1)*sizeof(char)); + for(i=0;il && !strncmp(argv[i],key,l) && argv[i][l] == '=') return 1; + return 0; +} + +/** + * add query parameter to array + * + * @param argcp pointer to array length int + * @param argvp pointer to array values + * @param addparam parameter to add (eg. "foo=bar") + */ +void oauth_add_param_to_array(int *argcp, char ***argvp, const char *addparam) { + (*argvp)=(char**) xrealloc(*argvp,sizeof(char*)*((*argcp)+1)); + (*argvp)[(*argcp)++]= (char*) xstrdup(addparam); +} + +/** + * + */ +void oauth_add_protocol(int *argcp, char ***argvp, + OAuthMethod method, + const char *c_key, //< consumer key - posted plain text + const char *t_key //< token key - posted plain text in URL + ){ + char oarg[1024]; + + // add OAuth specific arguments + if (!oauth_param_exists(*argvp,*argcp,"oauth_nonce")) { + char *tmp; + snprintf(oarg, 1024, "oauth_nonce=%s", (tmp=oauth_gen_nonce())); + oauth_add_param_to_array(argcp, argvp, oarg); + free(tmp); + } + + if (!oauth_param_exists(*argvp,*argcp,"oauth_timestamp")) { + snprintf(oarg, 1024, "oauth_timestamp=%li", (long int) time(NULL)); + oauth_add_param_to_array(argcp, argvp, oarg); + } + + if (t_key) { + snprintf(oarg, 1024, "oauth_token=%s", t_key); + oauth_add_param_to_array(argcp, argvp, oarg); + } + + snprintf(oarg, 1024, "oauth_consumer_key=%s", c_key); + oauth_add_param_to_array(argcp, argvp, oarg); + + snprintf(oarg, 1024, "oauth_signature_method=%s", + method==0?"HMAC-SHA1":method==1?"RSA-SHA1":"PLAINTEXT"); + oauth_add_param_to_array(argcp, argvp, oarg); + + if (!oauth_param_exists(*argvp,*argcp,"oauth_version")) { + snprintf(oarg, 1024, "oauth_version=1.0"); + oauth_add_param_to_array(argcp, argvp, oarg); + } + +#if 0 // oauth_version 1.0 Rev A + if (!oauth_param_exists(argv,argc,"oauth_callback")) { + snprintf(oarg, 1024, "oauth_callback=oob"); + oauth_add_param_to_array(argcp, argvp, oarg); + } +#endif + +} + +char *oauth_sign_url (const char *url, char **postargs, + OAuthMethod method, + const char *c_key, //< consumer key - posted plain text + const char *c_secret, //< consumer secret - used as 1st part of secret-key + const char *t_key, //< token key - posted plain text in URL + const char *t_secret //< token secret - used as 2st part of secret-key + ) { + return oauth_sign_url2(url, postargs, + method, NULL, + c_key, c_secret, + t_key, t_secret); +} + +char *oauth_sign_url2 (const char *url, char **postargs, + OAuthMethod method, + const char *http_method, //< HTTP request method + const char *c_key, //< consumer key - posted plain text + const char *c_secret, //< consumer secret - used as 1st part of secret-key + const char *t_key, //< token key - posted plain text in URL + const char *t_secret //< token secret - used as 2st part of secret-key + ) { + int argc; + char **argv = NULL; + char *rv; + + if (postargs) + argc = oauth_split_post_paramters(url, &argv, 0); + else + argc = oauth_split_url_parameters(url, &argv); + + rv=oauth_sign_array2(&argc, &argv, postargs, + method, http_method, + c_key, c_secret, t_key, t_secret); + + oauth_free_array(&argc, &argv); + return(rv); +} + +char *oauth_sign_array (int *argcp, char***argvp, + char **postargs, + OAuthMethod method, + const char *c_key, //< consumer key - posted plain text + const char *c_secret, //< consumer secret - used as 1st part of secret-key + const char *t_key, //< token key - posted plain text in URL + const char *t_secret //< token secret - used as 2st part of secret-key + ) { + return oauth_sign_array2 (argcp, argvp, + postargs, method, + NULL, + c_key, c_secret, + t_key, t_secret); +} + +void oauth_sign_array2_process (int *argcp, char***argvp, + char **postargs, + OAuthMethod method, + const char *http_method, //< HTTP request method + const char *c_key, //< consumer key - posted plain text + const char *c_secret, //< consumer secret - used as 1st part of secret-key + const char *t_key, //< token key - posted plain text in URL + const char *t_secret //< token secret - used as 2st part of secret-key + ) { + char oarg[1024]; + char *query; + char *okey, *odat, *sign; + char *http_request_method; + + if (!http_method) { + http_request_method = xstrdup(postargs?"POST":"GET"); + } else { + int i; + http_request_method = xstrdup(http_method); + for (i=0;i