/* ** minimal SSLProxy server for content switch project ** pieces of code taken from modssl which is protected under the below licence * ganesh kumar godavari (gkgodava@archie.uccs.edu) */ /* general libraries */ #include #include "msock.h" #include "prefork.h" #define MIN(a,b) (a>b) ? b : a static SSL *ssl=(SSL *) NULL; static SSL_CTX *ctx; /* contants definition for access from path */ char *log_file; char *ca_file; char *ca_path; char *key_file; char *cert_file; char *rand_file; union semun { int val; struct semid_ds *buf; ushort *array; }; int semID; /* ID of the semaphore set. */ union semun sem_val; /* semaphore value, for semctl(). */ static int long_jumped=0; static sigjmp_buf jmpbuf; static volatile sig_atomic_t canjump; static FILE *log_fp = (FILE *) NULL; static int sslVerifyCallback(int ok,X509 *xs,X509 *xi,int depth,int error,char *arg); static void serveRequest(int fd,struct front_server *remote_ip); void fatalError(char *format,...) { va_list args; va_start(args,format); (void) fprintf (stderr,"Error: "); vfprintf(stderr,format,args); va_end(args); exit(-1); } /* ** write all the junk!! which u think may be useful to Administrator ** */ void logPrint(FILE * log_fp,char * format,...) { va_list args; time_t now; char *time_string; if(log_fp == NULL) { (void) fprintf(stderr,"unable to identify how the log file %s closed \n",LOG_FILE); return; } now=time(NULL); time_string=ctime(&now); time_string[strlen(time_string)-1]='\0'; va_start(args,format); (void) fprintf(log_fp,"[%s]",time_string); (void) vfprintf(log_fp,format,args); va_end(args); fflush(log_fp); return; } static void sigAlarm(int sigtype) { if (canjump == 0) { return; } canjump=1; long_jumped=1; siglongjmp(jmpbuf,1); } static u_long pow(int a, int b) { u_long temp=0; if (b == 0) return 1; if (b == 1) return a; for ( ;b>1; b--) temp = temp + (a*a); return temp; } /* ** function converts hexadecimal string to a decimal integer. */ static u_long Hex(char *s) { int pos=0; u_long value=0; int len = strlen(s)-2; /* skipping the \r\n characters here*/ #if 0 fprintf(stderr,"the string to be converted to decimal from hexadecimal is %s",s); #endif for(pos = 0, s += (len-1),value=0 ;pos client speaks plain HTTP on our HTTPS */ (void)fprintf(stderr, "SSL handshake failed: HTTP spoken on HTTPS port; "); goto ExitProcessing; return 0; } else if (SSL_get_error(ssl, err) == SSL_ERROR_SYSCALL) { if (errno > 0) (void) fprintf(stderr,"SSL handshake interrupted by system " "[Hint: Stop button pressed in browser?!]\n"); else (void) fprintf(stderr, "Spurious SSL handshake interrupt" "[Hint: Usually just one of those OpenSSL confusions!?]\n"); goto ExitProcessing; return 0; } else if (err == -1) { fatalError("Error :%d unknown error in SSL_accept() %s\n",getpid(), ERR_error_string(ERR_get_error(),buf)); goto ExitProcessing; return 0; } } serveRequest(sock_fd,&szclient); ExitProcessing: SSL_set_shutdown(ssl,SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); SSL_smart_shutdown(ssl); SSL_free(ssl); ssl_mutex_file_close(); close(sock_fd); return 0; exit(1); } void preforkServer(u_short port) { int sockd, one = 1; struct sockaddr_in mysocket; struct linger li; bzero((char *)&mysocket,sizeof(mysocket)); mysocket.sin_port = htons(port); mysocket.sin_addr.s_addr = INADDR_ANY; mysocket.sin_family = AF_INET; if((sockd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0) { fprintf(stderr,"socket creation error socket\n"); exit(-1); } /* ** we will resuse port */ setsockopt(sockd,SOL_SOCKET,SO_REUSEADDR,(char *) &one, sizeof(int)); /* ** we will set keep alive. read Steven, Network Prog. 2nd edition, p186 */ one=1; setsockopt(sockd,SOL_SOCKET,SO_KEEPALIVE,(char *) &one, sizeof(int)); li.l_onoff=1; li.l_linger=30; setsockopt(sockd,SOL_SOCKET,SO_LINGER,(char *) &li, sizeof(struct linger)); if(bind(sockd,(struct sockaddr *)&mysocket,sizeof(mysocket)) < 0) { fprintf(stderr,"unable to bind\n"); exit( -1); } listen(sockd,5); { int len = sizeof(mysocket); if(getsockname(sockd,(struct sockaddr *)&mysocket,&len) < 0) { perror("getsockname"); exit(-1); } printf("bound to %d\n",ntohs(mysocket.sin_port)); fflush(stdout); } init_daemon("SSL_PROXY",(int) StartServers,(int) MaxClients,(int) MaxRequestsPerChild,(int) MaxSpareServers,(int) MinSpareServers,child_function,(int)sockd); exit(0); } int main(int argc, char **argv) { int err; char szbuf[BUFSIZ], issuer[256]; SSL_METHOD *method=SSLv23_server_method(); X509 *ca_cert, *client_cert; FILE *ca_fp=(FILE *) NULL; SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); (void)make_absolute_path(); /* convert relative to absolute path */ ctx=(SSL_CTX *) SSL_CTX_new(method); if (ctx == (SSL_CTX *) NULL) { fatalError("Unable to create new SSL CTX\n"); SSL_set_shutdown(ssl,SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); SSL_smart_shutdown(ssl); SSL_free(ssl); exit(1); } if (!SSL_CTX_load_verify_locations(ctx,ca_file,ca_path)) fatalError("Failed in SSL_CTX_load_verify_locations()!\n"); /* ** Load Private Key */ err=SSL_CTX_use_RSAPrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM); if (err == -1) { (void) fprintf(stderr,"Error reading private key\n"); SSL_CTX_free(ctx); exit(1); } /* ** Load Certificate */ err=SSL_CTX_use_certificate_file(ctx, cert_file, SSL_FILETYPE_PEM); if (err == -1) { (void) fprintf(stderr,"Error reading certificate\n"); SSL_CTX_free(ctx); exit(1); } /* ** check if the certificate and private key match */ err = SSL_CTX_check_private_key(ctx); if (err == -1) { (void) fprintf(stderr,"Error cerificate and private key donot match \n"); SSL_CTX_free(ctx); exit(1); } /* ** Load randomness */ if (!RAND_load_file(rand_file,1024*1024)) { (void) fprintf(stderr,"Unable to load Randomness for generating Entropy :-( \n"); } /* ** read the ca certificate and save the issuer string, we'll compare ** the client's issuer with this one, if they match allow connection ** or zap him */ ca_fp=fopen(ca_file,"r"); if (ca_fp == (FILE *) NULL) fatalError("Failed to open Trusted CA certificate file: %s\n", ca_file); ca_cert=NULL; ca_cert=X509_new(); if (!PEM_read_X509(ca_fp,&ca_cert,NULL,NULL)) fatalError("Error reading trusted CA certificate fie: %s\n",ca_file); X509_NAME_oneline(X509_get_issuer_name(ca_cert),issuer,256); if (issuer == (char *) NULL) fatalError("No issuer for trusted CA certificate file!\n"); if (ca_cert != NULL) X509_free(ca_cert); (void) fclose(ca_fp); /* open the log file */ if((log_fp=fopen(log_file,"a+"))== NULL) (void) fprintf(stderr, "unable to open the log file"); shm_id = shmget(IPC_PRIVATE, sizeof(struct load), IPC_CREAT | IPC_EXCL | 0600); if (shm_id == -1) { (void) fprintf(stderr,"Unable to allocate shared memory\n"); exit(1); } /* attach the shared memory segment to our process's address space. */ shm_addr = shmat(shm_id, NULL, 0); if (!shm_addr) /* operation failed. */ { (void) fprintf(stderr,"Error: unable to attach the shared memory to our process's address space\n "); exit(1); } /* create a semaphore */ semID = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600); if (semID == -1) { (void) fprintf(stderr,"Error: unable to create a semaphore\n"); exit(1); } /* intialize the semaphore to '1'. */ sem_val.val = 1; err = semctl(semID, 0, SETVAL, sem_val); if (err == -1) { (void) fprintf(stderr,"Error: unable to initialize the semaphore"); exit(1); } scoreboard = (struct load *) shm_addr; /* ** store the Session FIlename and file descriptors */ ssl_mutex_file_open(sess_file); open_critical_lock(); /* ** most cool Unix will take care of Zombies if we ignore SIGCHLD */ signal(SIGCHLD,SIG_IGN); signal(SIGPIPE,SIG_IGN); signal(SIGALRM,sigAlarm); noCoredump(); daemonize(); printf("TEST bf calling preforkServer\n"); /* ** open the server socket */ (void) preforkServer((u_short) SERVER_PORT); } /* ** here i am fixing the method and protocol name field to 10 bytes. i feel allocating 10 bytes is waste of "resource" */ static int parseRequest(char *req,char *method,char *url,char *proto,char *args,int len) { char *chp, *m, *u, *p; if (req == NULL || *req == '\0') return (-1); /* ** Method */ m=strtok(req,"\t "); if (m == NULL) return(-1); (void) strncpy(method,m,10); method[10]='\0'; /* ** Url ** Url can't have any spaces */ u=strtok(NULL,"\t\r "); if (u == NULL) return(-1); chp=strchr(u,'?'); if (chp) { *chp++ = '\0'; (void) strncpy(args,chp,len); } (void) strncpy(url,u,len); url[len]='\0'; /* ** HTTP Protocol version */ p=strtok(NULL,"\r "); if (p == NULL) return(-1); (void) strncpy(proto,p,10); proto[10]='\0'; return (0); /* OK */ } /* ** borrwed from thttpd */ static int hexDigit(char c) { if (strchr("0123456789abcdefABCDEF",c) != (char *) NULL) return (1); return (0); } /* ** find a substring from a string ** case insensitive */ int stristr(char *haystack,char *needle) { int i, j, k, l; for (i = 0; haystack[i]; i++) { for (j = 0, l = k = i; haystack[k] && needle[j] && tolower(haystack[k]) == tolower(needle[j]); j++, k++) ; if (needle[j] == '\0') return (i); } return -1; } /* ** returns the formated text i.e replacing the text containing source with dest ** none of the arguments are modified */ char *replace(char *text, char *source, char *dest) { char *nh,newtext[1024]; int start=0,end=0; if((start=stristr(text,source)) == -1) { (void) fprintf(stderr," unable to located substring %s in the string %s\n",text,source); } if((nh = (char *) malloc(1024))== NULL) { (void) fprintf(stderr, "unable to allocate memory for the header\n"); return NULL; } strncpy(nh,text,start); nh[start]='\0'; end = strlen(source); strcpy(newtext,nh); strcat(newtext,dest); strcat(newtext,(text+start+end)); strcpy(text,newtext); /* ** when i am freeing the memory occupied by headers and nh it is causing SIGSEGV(Segmentation Fault) only for I.E not for netscape ** damn these browers / damn my coding as one of these is wrong ** free(header); ** free(nh); */ return text; } void demoResponse(char *url) { char body[1024]; char content_length[64]; sprintf(body," 404 Not Found

Not Found

The requested URL %s was not found on this server.


Apache/1.3.14 Server at %s Port 80
",url,SERVER_NAME); sprintf(content_length,"Content-Length: %d\r\n",strlen(body)); sockPutsSSL(ssl,"HTTP/1.0 404 OK\r\n"); sockPutsSSL(ssl,"MIME-version: 1.0\r\n"); sockPutsSSL(ssl,content_length); sockPutsSSL(ssl,"Content-Type: text/html\r\n\r\n"); sockPutsSSL(ssl,body); return; } /* ** serverRequest is currently clumsy -- fix it and make it readable(gkgodava@archie.uccs.edu) */ static void serveRequest(int fd,struct front_server *remote_ip) { char header[1024], chunked_body[1024], *val, szcontent_type[BUFSIZ], ruleheader[BUFSIZ], szmethod[10], szurl[BUFSIZ], szproto[10], szargs[BUFSIZ], body[IOBUFFSIZE], TargetServer[128], szbuf[BUFSIZ]; int maxfd, nready, close_connection, chunked, content_length, server_sock_fd, chunk_size, status_code, portno, data_read; float version; struct timeval tv; Connection *conn_rec; u_long ret_addr; fd_set readfds; KEEPALIVE: /* if the client does not respond within the timeout limit, zap it */ FD_ZERO(&readfds); maxfd=SSL_get_fd(ssl)+1; FD_SET(SSL_get_fd(ssl),&readfds); content_length=0; data_read=0; *szcontent_type='\0'; *szmethod='\0'; *szurl='\0'; *szproto='\0'; *szargs='\0'; *szbuf='\0'; tv.tv_sec=CLIENT_TIMEOUT; tv.tv_usec=0; nready=select(maxfd,&readfds,NULL,NULL,&tv); if (nready == 0) { (void) fprintf(stderr," *** pid %d Timed out in %d seconds ***\n",getpid(),CLIENT_TIMEOUT); goto GracefulExit; } /* ** get request */ if (FD_ISSET(SSL_get_fd(ssl),&readfds)) { alarm(0); /* check whether the packet sent after the connection established has no data -- if so reject it */ if((sockGetsSSL(ssl,szbuf,sizeof(szbuf)-1)) <= 0) goto disGracefulExit; } fprintf(stderr,"TEST i1st package (%s) \n",szbuf); /* ** now we can allocate memory for the connection structue as we know it is not a junk request!! */ if( ((conn_rec = (struct Connection *) malloc(sizeof(Connection))) == NULL) || ((conn_rec->cli_ser = (struct front_server *) malloc(sizeof(struct front_server))) == NULL) || ((conn_rec->req = (struct Request *) malloc(sizeof(Request))) == NULL) ) { logPrint(log_fp,"Unable to create allocate memory for the connection structure !!\n"); goto GracefulExit; } memset(conn_rec,0,sizeof(conn_rec)); conn_rec->req->header=NULL; strcpy(header, mystrdup(szbuf)); fprintf(stderr,"TEST 1st package header (%s) \n",szbuf); if(parseRequest(szbuf,szmethod,szurl,szproto,szargs,BUFSIZ-1) != 0) { (void) fprintf(stderr," unable to process the request %s \n",szbuf); goto GracefulExit; } printf("TEST request %s", szbuf); logPrint(log_fp," request %s %s %s from %d \n",szmethod, szurl, szproto,remote_ip->addr); while(sockGetsSSL(ssl,szbuf,sizeof(szbuf)-1) > 0) { printf("TEST data %s", szbuf); logPrint(log_fp, "TEST data:"); logPrint(log_fp, szbuf); fprintf(stderr,"TEST data (%s) \n",szbuf); strcpy(szargs,szbuf); val=strchr(szargs,':'); if (val) { *val++ = '\0'; if (strncasecmp("content-length",szbuf,strlen("content-length")) == 0) content_length=atoi(val); if (strncasecmp("content-type",szbuf,strlen("content-type")) == 0) (void) strcpy(szcontent_type,val); if (strncasecmp("Host",szbuf,strlen("Host")) == 0) (void) sprintf(szbuf,"%s %s\r\n","Host:",SERVER_NAME); } strcat(header,szbuf); } if((conn_rec->req->header = (char *) malloc(strlen(header)+1)) == NULL) { (void) fprintf(stderr,"unable to allocate memory for conn->req->header\n"); goto GracefulExit; } (void) strcpy(conn_rec->req->header,header); if(content_length != 0) { if((conn_rec->req->body = (char *) malloc(content_length+1)) == NULL) { (void) fprintf(stderr,"unable to allocate memory for conn->req->body\n"); goto GracefulExit; } sockReadSSL(ssl,conn_rec->req->body,content_length); } else conn_rec->req->body=NULL; /* ** store the connection information as i need to perform rule matching */ memcpy(conn_rec->cli_ser,remote_ip,sizeof(struct front_server)); strcpy(conn_rec->req->method,szmethod); if ((conn_rec->req->url = (char *) malloc(strlen(szurl)+1)) == NULL) { (void) fprintf(stderr,"unable to allocate memory for conn->req->urlr\n"); goto GracefulExit; } (void) strcpy(conn_rec->req->url,szurl); if((conn_rec->req->content_type = (char *) malloc(strlen(szcontent_type)+1)) == NULL) { (void) fprintf(stderr,"unable to allocate memory for conn->req->content_type \r\n"); goto GracefulExit; } (void) strcpy(conn_rec->req->content_type,szcontent_type); (void) fprintf(stderr, "method= %s", conn_rec->req->method); fprintf(stderr, "TEST sending data to real server"); #if 0 /* ** performing the rule matching */ if((server_sock_fd=connectTo(RULE_SERVER_NAME,RULE_SERVER_PORT)) == -1) { (void) fprintf(stderr,"Failed to connect to the rule module %s\n",SERVER_NAME); if((server_sock_fd=connectTo(DEFAULT_RULE_SERVER_NAME,DEFAULT_RULE_SERVER_PORT )) == -1) { (void) fprintf(stderr,"Failed to connect to the rule module %s\n",SERVER_NAME); goto GracefulExit; } } (void) fprintf(stderr,"\n\nMethod: %s", conn_rec->req->method); (void) sprintf(ruleheader,"Port: %d\r\nClientAddr: %d\r\nMethod: %s\r\nUrl: %s\r\nContent-Type: %sContent-Length: %d\r\n\r\n", conn_rec->cli_ser->port, conn_rec->cli_ser->addr, conn_rec->req->method, conn_rec->req->url,conn_rec->req->content_type, content_length); /* send the request to the Rule Module */ sockWrite(server_sock_fd,ruleheader,strlen(ruleheader)); /*TEST out if(strcmp(conn_rec->req->method, "POST") == 0)*/ sockWrite(server_sock_fd,conn_rec->req->body,content_length); while(sockGets(server_sock_fd,szbuf,sizeof(szbuf)-1) > 0) { val=strchr(szbuf,':'); if (val) { *val++ = '\0'; if (strncasecmp("ServerAddress",szbuf,strlen("ServerAddress")) == 0) ret_addr=atol(val); if (strncasecmp("ServerName",szbuf,strlen("ServerName")) == 0) (void) strcpy(TargetServer,val); if (strncasecmp("Port",szbuf,strlen("Port")) == 0) portno =atol(val); } } close(server_sock_fd) ; TargetServer[strlen(TargetServer)-2] = '\0'; /* ** done with the rule matching */ if(szbuf == NULL) { (void) fprintf(stderr, "looks like server list has been modified\n"); goto GracefulExit; } conn_rec->req->header = mystrdup(replace(conn_rec->req->header,SERVER_NAME,TargetServer)); #endif /**TEST out if((server_sock_fd=connectTo(TargetServer,portno )) == -1)*/ /**TEST add the following line*/ if((server_sock_fd=connectTo(RULE_SERVER_NAME,RULE_SERVER_PORT)) == -1) { (void) fprintf(stderr,"Failed to connect to %s\n",RULE_SERVER_NAME); goto GracefulExit; } /* send the request to the real server */ sockPuts(server_sock_fd, conn_rec->req->header); sockPuts(server_sock_fd,"\r\n"); if(strcmp(conn_rec->req->method, "POST") == 0) sockPuts(server_sock_fd,conn_rec->req->body); /* freeing & initializing the values*/ content_length = 0; chunked = 0; close_connection = 0; status_code = 0; if((sockGets(server_sock_fd,szbuf,sizeof(szbuf)-1)) <= 0) goto GracefulExit; /* check for the status code */ sscanf(szbuf,"HTTP/%f %d \n",&version, &status_code); strcpy(header, mystrdup(szbuf)); while(sockGets(server_sock_fd,szbuf,sizeof(szbuf)-1) > 0) { strcpy(szargs,szbuf); val=strchr(szbuf,':'); if(val) { *val++ = '\0'; if(strncasecmp("content-length",szbuf,strlen("content-length")) == 0) content_length=atoi(val); if(strncasecmp("Connection",szbuf,strlen("Connection")) == 0) if( strncasecmp(val," close",strlen(" close")) == 0) close_connection = 1; /* ** trying to make sure we follow rfc 2616 section 19.4.6 ** HTTP/1.1 introduces the Transfer-Encoding header field. ** Proxies/gateways MUST remove any transfer-coding prior to ** forwarding a message via a MIME-compliant protocol. */ if(strncasecmp("Transfer-Encoding",szbuf,strlen("Transfer-Encoding")) == 0) { if( strncasecmp(val," chunked",strlen("chunked")) == 0) { chunked = 1; strcpy(szcontent_type,szargs); } } /* ** RFC 2616 sec 13.5.2 ** A transparent proxy MUST NOT modify any of the following fields in a ** request or response, and it MUST NOT add any of these fields if not ** already present: ** ** - Content-Location ** ** - Content-MD5 ** ** - ETag ** ** - Last-Modified ** */ /* i am violating this rule */ if(strncasecmp("Location",szbuf,strlen("Location")) == 0) { strcpy(szargs,replace(szargs,TargetServer,SERVER_NAME)); strcpy(szargs,replace(szargs,"http","https")); (void) fprintf(stderr,"\nthe new location bar is %s\n",szargs); } } strcat(header,szargs); } if(status_code > 400 ) { demoResponse(szurl); } else if(chunked == 1) { chunk_size=0; *chunked_body='\0'; content_length=0; /* chunked had more preference than content_length */ if ( sockGets(server_sock_fd,body,IOBUFFSIZE-1) < 0 ) fprintf(stderr, "unable to read chunked data"); while((chunk_size=Hex(body)) != 0) { fprintf(stderr, "szbuf %s chunck size %d \n",body,chunk_size); content_length = content_length + chunk_size; while ((chunk_size > 0) && ((data_read=sockRead(server_sock_fd,body,MIN(IOBUFFSIZE-1,chunk_size))) > 0 )) { chunk_size = chunk_size - data_read; strcat(chunked_body,body); } if (sockGets(server_sock_fd,body,IOBUFFSIZE-1) < 0 ) fprintf(stderr, "unable to read chunked data\n"); } /* making changes to the header */ sprintf(szbuf,"Content-Length: %d\r\n",content_length); strcpy(szargs, replace(header,szcontent_type,szbuf)); strcpy(header,szargs); } if(status_code < 400) { /* ** sending headers to the client */ strcat(header,"\r\n\0"); sockWriteSSL(ssl,header,strlen(header)); /* **sending the chunked data as body if the header contains Tansfer-Encoding: chunked\r\n */ if(chunked == 1) { sockWriteSSL(ssl,chunked_body,content_length); /* (void) fprintf(stderr," body is %s\n",chunked_body); */ content_length =0; } for(data_read=0; content_length > 0;data_read=0 ) { if ((data_read = iread(server_sock_fd,body,IOBUFFSIZE-1)) < 0) { if (errno == ECONNRESET) (void) fprintf(stderr,"\nconnection was reset by the server\n"); else (void) fprintf(stderr,"\nunable to read data from the socket\n"); goto disGracefulExit; } content_length -= data_read; if(data_read != sockWriteSSL(ssl,body,data_read)) { (void) fprintf(stderr,"pid %d unable to pump more data on content length\n",getpid()); goto disGracefulExit; } } } if(close_connection == 1) goto GracefulExit; else { free(conn_rec->req->url); free(conn_rec->req->header); free(conn_rec->req->content_type); free(conn_rec->req->body); free(conn_rec->req); #if 0 free(conn_rec->cli_ser->name); #endif free(conn_rec->cli_ser); (void) close(server_sock_fd); goto KEEPALIVE; } #if 0 (void) demoResponse(); #endif GracefulExit: free(conn_rec->req->url); free(conn_rec->req->header); free(conn_rec->req->content_type); free(conn_rec->req->body); free(conn_rec->req); #if 0 free(conn_rec->cli_ser->name); #endif free(conn_rec->cli_ser); disGracefulExit: (void) close(server_sock_fd); (void) close(SSL_get_fd(ssl)); /* closing the connection*/ #if 0 (void) fprintf(stderr,"\n\n closing the ssl context bye bye %d ..\n",getpid()); #endif return; } static int sslVerifyCallback(int ok,X509 *xs,X509 *xi,int depth,int error, char *arg) { char *cip, *issuer_ca, s[64]; X509_NAME_oneline(X509_get_subject_name(xs),s,64); if (s != (char *) NULL) { if (ok) { (void) fprintf(stderr,"ok depth=%d,subject=%s\n",depth,s); } else (void) fprintf (stderr,"depth=%d error=%d,subject=%s\n", depth,error,s); free(s); } X509_NAME_oneline(X509_get_issuer_name(xs),s,64); if (s == (char *) NULL) { (void) fprintf (stderr,"verify error\n"); ok=0; } (void) fprintf(stderr,"Issuer=%s\n",s); ok=1; return (ok); }