U8_EXPORT /* u8_grow_input_stream: Arguments: a pointer to an input stream and max buffer size Returns: the size of the new buffer Doubles the size of the input buffer */ ssize_t u8_grow_input_stream(struct U8_INPUT *in,ssize_t to_size) { int owns_buf = ((in->u8_streaminfo)&(U8_STREAM_OWNS_BUF)); size_t max = in->u8_bufsz, bytes_buffered = in->u8_inlim - in->u8_read, bytes_read = in->u8_read - in->u8_inbuf; size_t new_max = ((max>=U8_BUF_THROTTLE_POINT)? (max+(U8_BUF_THROTTLE_POINT/2)): (max*2)); if (to_size<0) to_size=new_max; else if ((to_size+U8_BUF_MIN_GROW)<max) return max; else {} while (new_max<to_size) new_max = ((new_max>=U8_BUF_THROTTLE_POINT)? (new_max+(U8_BUF_THROTTLE_POINT/2)): (new_max*2)); u8_byte *buf = in->u8_inbuf, *new_buf=NULL; /* Reset buffer */ if (bytes_read>0) { memcpy(buf, in->u8_read, bytes_buffered); in->u8_read = buf; in->u8_inlim -= bytes_read; *(in->u8_inlim) = '\0';} /* Try to allocate a new buffer */ new_buf=(owns_buf)?(u8_realloc(buf,new_max)):(u8_malloc(new_max)); if (new_buf==NULL) { size_t shrink_by = (new_max-max)/16; while (new_buf==NULL) { /* The alloc/realloc call failed, so try smaller sizes */ errno=0; /* Reset errno */ /* Shrink until it works or the buffer size is smaller than the current value */ new_max=new_max-shrink_by; if (new_max<=max) { u8_log(LOGCRIT,"MallocFailed/grow_output", "Couldn't grow buffer for input stream"); return max;} u8_log(LOGCRIT,"MemoryRestricted", "Couldn't grow u8_input_stream buffer (x%llx) to %lld bytes, trying %lld", (long long) in,new_max+shrink_by,new_max); new_buf=(owns_buf)?(u8_realloc(in->u8_inbuf,new_max)): (u8_malloc(new_max));}} if (!(owns_buf)) { memcpy(new_buf,buf,bytes_buffered); in->u8_streaminfo|=U8_STREAM_OWNS_BUF;} in->u8_inbuf=new_buf; in->u8_read=new_buf; in->u8_inlim=new_buf+bytes_buffered; in->u8_bufsz=new_max; return new_max; }
U8_EXPORT /* u8_grab_bytes: Arguments: a pointer to a string Returns: an ASCII string representation of some number of bytes from a string */ char *u8_grab_bytes(u8_string s,int n,char *buf) { char *result=((buf)?(buf):(u8_malloc(n))), *write=result, *lim=result+(n-1); const u8_byte *scan=s; int c=*scan++; while ((c>0)&&(write<lim)) { if ((c<128)&&((isprint((char)c))||(c==32))) *write++=c; else if ((write+2)>lim) break; else if ((c=='\n')||(c=='\t')||(c=='\r')) { char *s; if (c=='\n') s="\\n"; else if (c=='\t') s="\\t"; else s="\\r"; memcpy(write,s,3); write=write+2; } else if ((write+3)>lim) break; else { char buf[8]; sprintf(buf,"%%%02x",c); memcpy(write,buf,4); write=write+3; } c=*scan++; } *write++='\0'; return result; }
/* Inserts a string at the beginning and before every line break in another string */ U8_EXPORT u8_string u8_indent_text(u8_string input,u8_string indent) { int len=strlen(input), n_lines=1, indent_len=strlen(indent); const u8_byte *scan=input; u8_byte *output; while ((scan=strchr(scan+1,'\n'))) n_lines++; output=u8_malloc(len+(n_lines*indent_len)+1); if (output==NULL) { errno=0; return NULL; } else { const u8_byte *read=input; scan=read; u8_byte *write=output; while ((scan=strchr(read,'\n'))) { int n_bytes=scan-read; strncpy(write,read,n_bytes); write=write+n_bytes; *write++='\n'; strncpy(write,indent,indent_len); write=write+indent_len; read=scan+1; } strcpy(write,read); return (u8_string)output; } }
U8_EXPORT void u8_grow_contour(u8_contour c) { if (c->u8c_max_blocks==0) { c->u8c_blocks=u8_malloc(U8_CONTOUR_INIT_N_BLOCKS*sizeof(void *)); c->u8c_max_blocks=U8_CONTOUR_INIT_N_BLOCKS;} else if ((c->u8c_flags)&(U8_CONTOUR_BLOCKS_MALLOCD)) { int new_max=c->u8c_max_blocks*2; void **new_blocks=u8_realloc(c->u8c_blocks,new_max*sizeof(void *)); c->u8c_blocks=new_blocks; c->u8c_max_blocks=new_max;} else { int new_max=c->u8c_max_blocks*2; void **new_blocks=u8_malloc(new_max*sizeof(void *)); memcpy(new_blocks,c->u8c_blocks,sizeof(void *)*(c->u8c_n_blocks)); c->u8c_blocks=new_blocks; c->u8c_max_blocks=new_max;} }
u8_string u8_strndup(u8_string s,int len) { int newlen=len+1; newlen=(((newlen%4)==0)?(newlen):(((newlen/4)+1)*4)); u8_byte *nstring=u8_malloc(newlen); strncpy(nstring,s,len); nstring[len]='\0'; return (u8_string)nstring; }
U8_EXPORT unsigned char *u8_random_vector(int len) { unsigned char *vec=u8_malloc(len); if (vec) { int i=0; while (i<len) { int randomval=u8_random(256); vec[i++]=randomval;} return vec;} else return vec; }
/* This isn't entirely threadsafe */ static void add_cc_cipher(u8_string name,CCAlgorithm alg,CCOptions opts, ssize_t keymin,ssize_t keymax, ssize_t ivlen,ssize_t blocksize) { if (n_ciphers<MAX_CIPHERS) { u8_cipher fresh=u8_malloc(sizeof(struct U8_CCCIPHER)); fresh->cc_name=name; fresh->cc_algorithm=alg; fresh->cc_opts=opts; fresh->cc_keymin=keymin; fresh->cc_keymax=keymax; fresh->cc_ivlen=ivlen; fresh->cc_blocksize=blocksize; ciphers[n_ciphers++]=fresh;} else u8_log(LOG_CRIT,_("TooManyCiphers"),"Can't declare cipher#%d %s", n_ciphers,name); }
U8_EXPORT u8_string u8_parse_addr (u8_string spec,int *portp,u8_byte *result,ssize_t buflen) { u8_byte *split=strchr(spec,'@'); int len=strlen(spec); if ((result==NULL)||(buflen<0)) { buflen=len+1; result=u8_malloc(buflen); } if (split==spec) { *portp=0; strcpy(result,spec+1); return result;} else if (split) { int portlen=split-spec, hostlen=len-(portlen+1); u8_byte _portspec[32], *portspec; if (portlen>31) portspec=u8_malloc(1+(split-spec)); else portspec=_portspec; strncpy(portspec,spec,portlen); portspec[portlen]='\0'; *portp=u8_get_portno(portspec); if (portspec!=_portspec) u8_free(portspec); if (hostlen>=buflen) result=u8_malloc(hostlen+1); strncpy(result,split+1,hostlen); result[hostlen]='\0'; return result;} else if ((split=strrchr(spec,':'))) { /* We search backwards for the colon because we want to handle IPv6 addresses with double colons separating octets (?) */ if ((split[1]=='\0')||((split>spec)&&(split[-1]==':'))) { *portp=0; strcpy(result,spec+1); return result;} *portp=u8_get_portno(split+1); if (buflen<(split-spec)) { buflen=(split-spec)+1; result=u8_malloc(buflen);} strncpy(result,spec,split-spec); if (result[split-spec-1]==':') /* Accept a double colon before the port number, in case the address portion is an IPV6 numeric address. */ result[split-spec-1]='\0'; else result[split-spec]='\0'; return result;} else return NULL; }
static struct hostent *copy_hostent(struct hostent *he) { struct hostent *copy; char *buf, *alloc; int n_bytes=sizeof(struct hostent); int addr_len=he->h_length, n_aliases=0, n_addrs=0; char **scanner, **writer; n_bytes=n_bytes+strlen(he->h_name)+1; /* Count the space for the aliases, counting each string. */ scanner=he->h_aliases; while (*scanner) { n_bytes=n_bytes+strlen(*scanner)+1; scanner++; n_aliases++;} n_bytes=n_bytes+sizeof(char *)*(n_aliases+1); scanner=he->h_addr_list; while (*scanner) { n_bytes=n_bytes+addr_len; scanner++; n_addrs++;} n_bytes=n_bytes+sizeof(char *)*(n_addrs+1); alloc=buf=u8_malloc(n_bytes); copy=(struct hostent *)buf; copy->h_length=he->h_length; copy->h_addrtype=he->h_addrtype; alloc=buf+sizeof(struct hostent); /* Copy aliases */ writer=copy->h_aliases=(char **) alloc; alloc=alloc+sizeof(char *)*(n_aliases+1); scanner=he->h_aliases; while (*scanner) { int len=strlen(*scanner); strcpy(alloc,*scanner); *writer++=alloc; alloc=alloc+len+1; scanner++;} *writer++=NULL; /* Copy addrs */ writer=copy->h_addr_list=(char **) alloc; alloc=alloc+sizeof(char *)*(n_addrs+1); scanner=he->h_addr_list; while (*scanner) { memcpy(alloc,*scanner,addr_len); *writer++=alloc; alloc=alloc+addr_len; scanner++;} *writer++=NULL; /* Copy main name */ { int namelen=strlen(he->h_name); strcpy(alloc,he->h_name); copy->h_name=alloc; alloc=alloc+namelen+1;} if (alloc != buf+n_bytes) { u8_raise(InternalError,"copy_hostent",NULL); return NULL;} else return copy; }
U8_EXPORT /* u8_gets_x: Arguments: a pointer to a character buffer, it size, an input stream, a terminating string, and a pointer to a size value. Returns: a string or NULL. Reads UTF-8 u8_inbuf UTF-8 from an input stream up until *eos* (which might be, for instance, a newline "\n") or stream end and null-terminates the result. If buf is provided and has enough space (according to len), it is used for the data. Otherwise, the function returns NULL. If buf is NULL, a string is allocated with u8_malloc and used to store the results. In either case, the number of u8_inbuf needed is stored on the indicated size pointer. */ u8_string u8_gets_x(u8_byte *buf,int len, struct U8_INPUT *f,u8_string eos, int *sizep) { const u8_byte *found=NULL, *start=f->u8_read, *end=NULL; int size, ec=-1; if (f->u8_read>=f->u8_inlim) { if (sizep) *sizep=0; return NULL;} while (((found=strstr(start,eos))==NULL)||(found>f->u8_inlim)) { int start_pos=f->u8_inlim-f->u8_read, retval=0; /* Quit if we have length constraints which we are already past. */ if (f->u8_fillfn) retval=f->u8_fillfn(f); if (retval==0) break; else if (retval<0) { if (sizep) *sizep=retval; return NULL;} start=f->u8_read+start_pos;} if (found) { size=(found-f->u8_read); if (sizep) *sizep=size; /* No data, return NULL */ if ((buf) && (size>=len)) return NULL; else if (buf==NULL) buf=u8_malloc(size+1); if (size) u8_getn(buf,size,f); else buf[0]='\0'; /* Advance past the separator */ f->u8_read=f->u8_read+strlen(eos); return buf;} else return NULL; }
U8_EXPORT /* u8_slice: Arguments: two pointers into a UTF8-encoded string Returns: the substring between the pointers */ u8_string u8_slice(const u8_byte *start,const u8_byte *end) { if (end<start) return NULL; else if (end-start>65536*8) return NULL; else { unsigned int newlen=(end-start)+1; u8_byte *slice; newlen=(((newlen%4)==0)?(newlen):(((newlen/4)+1)*4)); slice=u8_malloc(newlen); strncpy(slice,start,(end-start)); slice[end-start]='\0'; return (u8_string)slice;} }
U8_EXPORT struct hostent *u8_gethostbyname(u8_string hname,int type) { char *name=u8_tolibc(hname); struct hostent *fetched, *copied; char _buf[1024], *buf=_buf; int bufsiz=0, herrno=0, retval; #if HAVE_GETHOSTBYNAME2_R struct hostent _fetched, *result; fetched=&_fetched; if (type>0) retval= gethostbyname2_r(name,type,fetched,buf,1024,&result,&herrno); else retval=gethostbyname_r(name,fetched,buf,1024,&result,&herrno); while (retval==ERANGE) { if (bufsiz) bufsiz=2048; else {u8_free(buf); bufsiz=bufsiz*2;} buf=u8_malloc(bufsiz); if (type>0) retval= gethostbyname2_r(name,type,fetched,buf,1024,&result,&herrno); else retval=gethostbyname_r(name,fetched,buf,1024,&result,&herrno);} if (result==NULL) { if (bufsiz) u8_free(buf); u8_graberr(herrno,"u8_gethostbyname",u8_strdup(hname)); return NULL;} copied=((fetched==NULL) ? (NULL) :(copy_hostent(fetched))); if (bufsiz) u8_free(buf); #else u8_lock_mutex(&netfns_lock); fetched=gethostbyname(name); if (fetched==NULL) { u8_seterr(UnknownHost,"u8_gethostbyname",name); u8_unlock_mutex(&netfns_lock); return NULL;} copied=copy_hostent(fetched); u8_unlock_mutex(&netfns_lock); #endif u8_free(name); return copied; }
U8_EXPORT unsigned char *u8_decrypt (const unsigned char *input,size_t len, const char *cipher, const unsigned char *key,size_t keylen, const unsigned char *iv,size_t ivlen, size_t *result_len) { ssize_t bytecount; struct U8_BYTEBUF in, out; unsigned char *outbuf=u8_malloc(2*len); memset(&in,0,sizeof(in)); memset(&out,0,sizeof(in)); in.u8_direction=u8_input_buffer; in.u8_buf=in.u8_ptr=(u8_byte *)input; in.u8_lim=(u8_byte *)(input+len); in.u8_direction=u8_output_buffer; out.u8_growbuf=len; out.u8_ptr=out.u8_buf=outbuf; out.u8_lim=outbuf+2*len; bytecount=u8_cryptic (0,cipher,key,keylen,iv,ivlen, (u8_block_reader)u8_bbreader,(u8_block_writer)u8_bbwriter, &in,&out,"u8_decrypt"); if (bytecount<0) return NULL; else *result_len=bytecount; return out.u8_buf; }
U8_EXPORT ssize_t u8_cryptic (int do_encrypt,const char *cname, const unsigned char *key,int keylen, const unsigned char *iv,int ivlen, u8_block_reader reader,u8_block_writer writer, void *readstate,void *writestate, u8_context caller) { if (strncasecmp(cname,"rsa",3)==0) { ENGINE *eng=ENGINE_get_default_RSA(); EVP_PKEY _pkey, *pkey; EVP_PKEY_CTX *ctx; int pubkeyin=(strncasecmp(cname,"rsapub",6)==0); const unsigned char *scankey=key; struct U8_BYTEBUF bb; int retval=-1; if (pubkeyin) pkey=d2i_PUBKEY(NULL,&scankey,keylen); else pkey=d2i_PrivateKey((EVP_PKEY_RSA),NULL,&scankey,keylen); if (!(pkey)) ctx=NULL; else { #if (OPENSSL_VERSION_NUMBER>=0x1000204fL) ctx=EVP_PKEY_CTX_new(pkey,eng); #else ctx=EVP_PKEY_CTX_new(pkey,NULL); #endif } if (ctx) { memset(&bb,0,sizeof(bb)); bb.u8_direction=u8_output_buffer; bb.u8_buf=bb.u8_ptr=(u8_byte *)u8_malloc(1024); bb.u8_lim=(u8_byte *)(bb.u8_buf+1024); bb.u8_growbuf=1; fill_bytebuf(&bb,reader,readstate);} if (!(ctx)) {} else if ((pubkeyin)? ((do_encrypt)?((retval=EVP_PKEY_encrypt_init(ctx))<0): ((retval=EVP_PKEY_verify_recover_init(ctx))<0)): ((do_encrypt)?((retval=EVP_PKEY_sign_init(ctx))<0): ((retval=EVP_PKEY_decrypt_init(ctx))<0))) {} else { unsigned char *in=bb.u8_buf; size_t inlen=bb.u8_ptr-bb.u8_buf; unsigned char *out=NULL; size_t outlen; if (pubkeyin) { if (do_encrypt) retval=EVP_PKEY_encrypt(ctx,NULL,&outlen,in,inlen); else retval=EVP_PKEY_verify_recover(ctx,NULL,&outlen,in,inlen);} else if (do_encrypt) retval=EVP_PKEY_sign(ctx,NULL,&outlen,in,inlen); else retval=EVP_PKEY_decrypt(ctx,NULL,&outlen,in,inlen); if (retval<0) {} else if ((out=u8_malloc(outlen))==NULL) {} else if (pubkeyin) { if (do_encrypt) retval=EVP_PKEY_encrypt(ctx,out,&outlen,in,inlen); else retval=EVP_PKEY_verify_recover(ctx,out,&outlen,in,inlen);} else if (do_encrypt) retval=EVP_PKEY_sign(ctx,out,&outlen,in,inlen); else retval=EVP_PKEY_decrypt(ctx,out,&outlen,in,inlen); if (retval<0) {} else retval=writer(out,outlen,writestate); if (out) u8_free(out);} u8_free(bb.u8_buf); if (retval<0) { unsigned long err=ERR_get_error(); char buf[512]; buf[0]='\0'; ERR_error_string_n(err,buf,512); u8_seterr(u8_InternalCryptoError,OPENSSL_CRYPTIC,u8_fromlibc((char *)buf)); ERR_clear_error();} if (ctx) EVP_PKEY_CTX_free(ctx); if (pkey) EVP_PKEY_free(pkey); return retval;} else { EVP_CIPHER_CTX ctx; int inlen, outlen, retval=0; ssize_t totalout=0, totalin=0; unsigned char inbuf[1024], outbuf[1024+EVP_MAX_BLOCK_LENGTH]; const EVP_CIPHER *cipher=((cname)?(EVP_get_cipherbyname(cname)): (EVP_aes_128_cbc())); if (cipher) { int needkeylen=EVP_CIPHER_key_length(cipher); int needivlen=EVP_CIPHER_iv_length(cipher); int blocksize=EVP_CIPHER_block_size(cipher); if (blocksize>1024) blocksize=1024; u8_log(CRYPTO_LOGLEVEL,OPENSSL_CRYPTIC, " %s cipher=%s, keylen=%d/%d, ivlen=%d/%d, blocksize=%d\n", ((do_encrypt)?("encrypt"):("decrypt")), cname,keylen,needkeylen,ivlen,needivlen,blocksize); if ((needivlen)&&(ivlen)&&(ivlen!=needivlen)) return u8_reterr(u8_BadCryptoIV, ((caller)?(caller):(OPENSSL_CRYPTIC)), u8_mkstring("%d!=%d(%s)",ivlen,needivlen,cname)); memset(&ctx,0,sizeof(ctx)); EVP_CIPHER_CTX_init(&ctx); retval=EVP_CipherInit(&ctx, cipher, NULL, NULL, do_encrypt); if (retval==0) return u8_reterr(u8_CipherInit_Failed, ((caller)?(caller):(OPENSSL_CRYPTIC)), u8_strdup(cname)); retval=EVP_CIPHER_CTX_set_key_length(&ctx,keylen); if (retval==0) return u8_reterr(u8_BadCryptoKey, ((caller)?(caller):(OPENSSL_CRYPTIC)), u8_mkstring("%d!=%d(%s)",keylen,needkeylen,cname)); if ((needivlen)&&(ivlen!=needivlen)) return u8_reterr(u8_BadCryptoIV, ((caller)?(caller):(OPENSSL_CRYPTIC)), u8_mkstring("%d!=%d(%s)",ivlen,needivlen,cname)); retval=EVP_CipherInit(&ctx, cipher, key, iv, do_encrypt); if (retval==0) return u8_reterr(u8_CipherInit_Failed, ((caller)?(caller):(OPENSSL_CRYPTIC)), u8_strdup(cname)); while (1) { inlen = reader(inbuf,blocksize,readstate); if (inlen <= 0) { u8_log(CRYPTO_LOGLEVEL,OPENSSL_CRYPTIC, "Finished %s(%s) with %ld in, %ld out", ((do_encrypt)?("encrypt"):("decrypt")), cname,totalin,totalout); break;} else totalin=totalin+inlen; if (!(EVP_CipherUpdate(&ctx,outbuf,&outlen,inbuf,inlen))) { char *details=u8_malloc(256); unsigned long err=ERR_get_error(); ERR_error_string_n(err,details,256); EVP_CIPHER_CTX_cleanup(&ctx); return u8_reterr(u8_InternalCryptoError, ((caller)?(caller):((u8_context)"u8_cryptic")), details);} else { u8_log(CRYPTO_LOGLEVEL,OPENSSL_CRYPTIC, "%s(%s) consumed %d/%ld bytes, emitted %d/%ld bytes" " in=<%v>\n out=<%v>", ((do_encrypt)?("encrypt"):("decrypt")),cname, inlen,totalin,outlen,totalout+outlen, inbuf,inlen,outbuf,outlen); writer(outbuf,outlen,writestate); totalout=totalout+outlen;}} if (!(EVP_CipherFinal(&ctx,outbuf,&outlen))) { char *details=u8_malloc(256); unsigned long err=ERR_get_error(); ERR_error_string_n(err,details,256); EVP_CIPHER_CTX_cleanup(&ctx); return u8_reterr(u8_InternalCryptoError, ((caller)?(caller):(OPENSSL_CRYPTIC)), details);} else { writer(outbuf,outlen,writestate); u8_log(CRYPTO_LOGLEVEL,OPENSSL_CRYPTIC, "%s(%s) done after consuming %ld/%ld bytes, emitting %ld/%ld bytes" "\n final out=<%v>", ((do_encrypt)?("encrypt"):("decrypt")),cname, inlen,totalin,outlen,totalout+outlen, outbuf,outlen); EVP_CIPHER_CTX_cleanup(&ctx); totalout=totalout+outlen; return totalout;}} else { char *details=u8_malloc(256); unsigned long err=ERR_get_error(); ERR_error_string_n(err,details,256); return u8_reterr("Unknown cipher", ((caller)?(caller):((u8_context)"u8_cryptic")), details);} } }