From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 15448 invoked by alias); 1 Apr 2009 09:48:37 -0000 Received: (qmail 15324 invoked by alias); 1 Apr 2009 09:48:33 -0000 X-SWARE-Spam-Status: No, hits=-0.1 required=5.0 tests=AWL,BAYES_00,J_CHICKENPOX_33,J_CHICKENPOX_43,J_CHICKENPOX_44,J_CHICKENPOX_65,J_CHICKENPOX_66,SPF_HELO_PASS X-Spam-Status: No, hits=-0.1 required=5.0 tests=AWL,BAYES_00,J_CHICKENPOX_33,J_CHICKENPOX_43,J_CHICKENPOX_44,J_CHICKENPOX_65,J_CHICKENPOX_66,SPF_HELO_PASS X-Spam-Check-By: sourceware.org X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on bastion.fedora.phx.redhat.com Subject: cluster: STABLE3 - fence_agents: Replaced telnet_ssl by fence_nss_wrapper To: cluster-cvs-relay@redhat.com X-Project: Cluster Project X-Git-Module: cluster.git X-Git-Refname: refs/heads/STABLE3 X-Git-Reftype: branch X-Git-Oldrev: 451c53d7b8f92e6e35bf871606a5b7286852b9a7 X-Git-Newrev: a68e594ae0461a82b8364b2a88e0698c0b0cad46 From: Jan Friesse Message-Id: <20090401094816.3286D120346@lists.fedorahosted.org> Date: Wed, 01 Apr 2009 09:48:00 -0000 X-Scanned-By: MIMEDefang 2.58 on 172.16.52.254 Mailing-List: contact cluster-cvs-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Post: List-Help: , Sender: cluster-cvs-owner@sourceware.org X-SW-Source: 2009-q2/txt/msg00002.txt.bz2 Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=a68e594ae0461a82b8364b2a88e0698c0b0cad46 Commit: a68e594ae0461a82b8364b2a88e0698c0b0cad46 Parent: 451c53d7b8f92e6e35bf871606a5b7286852b9a7 Author: Jan Friesse AuthorDate: Wed Mar 25 17:00:43 2009 +0100 Committer: Jan Friesse CommitterDate: Wed Apr 1 11:47:50 2009 +0200 fence_agents: Replaced telnet_ssl by fence_nss_wrapper Fence_nss_wrapper is drop-on replacement of telnet_ssl. This is hobbit like tool with support for NSS (SSL) connection. In contrast of old tool, this has many advantages: - Support for IPv6 (user can force IPv4) - Use polling instead of active waiting (non-blocking socket + sleep) - It's based on NSS (future of Fedora SSL) - Support for service names (not used in agents) --- fence/agents/lib/Makefile | 2 +- fence/agents/lib/fencing.py.py | 2 +- fence/agents/lib/telnet_ssl.py | 72 ---- fence/agents/nss_wrapper/Makefile | 24 ++ fence/agents/nss_wrapper/fence_nss_wrapper.c | 483 ++++++++++++++++++++++++++ make/fencebuild.mk | 1 + 6 files changed, 510 insertions(+), 74 deletions(-) diff --git a/fence/agents/lib/Makefile b/fence/agents/lib/Makefile index 049ff6e..2cccd98 100644 --- a/fence/agents/lib/Makefile +++ b/fence/agents/lib/Makefile @@ -1,6 +1,6 @@ include ../../../make/defines.mk -TARGET= fencing.py telnet_ssl fencing_snmp.py +TARGET= fencing.py fencing_snmp.py FENCEAGENTSLIB= $(TARGET) diff --git a/fence/agents/lib/fencing.py.py b/fence/agents/lib/fencing.py.py index e5473e1..34b0caa 100644 --- a/fence/agents/lib/fencing.py.py +++ b/fence/agents/lib/fencing.py.py @@ -30,7 +30,7 @@ EC_STATUS_HMC = 9 TELNET_PATH = "/usr/bin/telnet" SSH_PATH = "/usr/bin/ssh" -SSL_PATH = "@FENCEAGENTSLIBDIR@/telnet_ssl" +SSL_PATH = "@SBINDIR@/fence_nss_wrapper" all_opt = { "help" : { diff --git a/fence/agents/lib/telnet_ssl.py b/fence/agents/lib/telnet_ssl.py deleted file mode 100644 index 5d0c981..0000000 --- a/fence/agents/lib/telnet_ssl.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/python - -##### -## simple telnet client with SSL support -## -## ./telnet_ssl host port -##### - -import sys, socket, string, fcntl, os , time -from OpenSSL import SSL - -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - -def main(): - hostname = None - port = None - - if (len(sys.argv) != 3): - print "Error: You have to enter hostname and port number\n" - sys.exit(-1) - - hostname = sys.argv[1] - port = int(sys.argv[2]) - - try: - s = socket.socket (socket.AF_INET, socket.SOCK_STREAM) - s.connect((hostname,port)) - ctx = SSL.Context(SSL.SSLv23_METHOD) - conn = SSL.Connection(ctx, s) - conn.set_connect_state() - except socket.error, e: - print "Error: Unable to connect to %s:%s %s" % (hostname, port, str(e)) - sys.exit(-1) - - fcntl.fcntl(sys.stdin, fcntl.F_SETFL, os.O_NONBLOCK) - s.settimeout(0) - - while 1: - try: - write_buff = sys.stdin.readline() - if (len(write_buff) > 0): - write_buff = string.rstrip(write_buff) - i = 10 - while i > 0: - i = i-1 - try: - conn.send(write_buff + "\r\n") - i = -1 - except SSL.WantReadError: - ## We have to wait for connect, mostly just for first time - time.sleep(1) - if i == 0: - sys.exit(-2) - except IOError: - 1 - - try: - read_buff = conn.recv(4096) - print read_buff - sys.stdout.flush() - except SSL.WantReadError: - 1 - except SSL.ZeroReturnError: - break - - -if __name__ == "__main__": - main() diff --git a/fence/agents/nss_wrapper/Makefile b/fence/agents/nss_wrapper/Makefile new file mode 100644 index 0000000..ae74124 --- /dev/null +++ b/fence/agents/nss_wrapper/Makefile @@ -0,0 +1,24 @@ +TARGET= fence_nss_wrapper + +SBINDIRT=$(TARGET) + +all: ${TARGET} + +include ../../../make/defines.mk +include $(OBJDIR)/make/cobj.mk +include $(OBJDIR)/make/clean.mk +include $(OBJDIR)/make/install.mk +include $(OBJDIR)/make/uninstall.mk + +OBJS= fence_nss_wrapper.o + +CFLAGS += -I${incdir} -I${nsprincdir} -I${nssincdir} + +LDFLAGS += -L${libdir} -L${nsprlibdir} -L${nsslibdir} -lnss3 -lssl3 + +${TARGET}: ${OBJS} + $(CC) -o $@ $^ $(LDFLAGS) + +clean: generalclean + +-include $(OBJS:.o=.d) diff --git a/fence/agents/nss_wrapper/fence_nss_wrapper.c b/fence/agents/nss_wrapper/fence_nss_wrapper.c new file mode 100644 index 0000000..c6b3cda --- /dev/null +++ b/fence/agents/nss_wrapper/fence_nss_wrapper.c @@ -0,0 +1,483 @@ +/** @file fence_nss_wrapper.c - Main source code of hobbit like tool with + support for NSS (SSL) connection. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*---- CONSTANTS -------------*/ + +/** Default operation = connect and telnet*/ +#define OPERATION_DEFAULT 0 +/** Operation display help*/ +#define OPERATION_HELP 1 + +/** Default mode of connection. Try first found working address*/ +#define MODE_DEFAULT 3 +/** Use only IPv4*/ +#define MODE_IP4MODE 1 +/** Use only IPv6*/ +#define MODE_IP6MODE 2 +/** Use RAW mode - no change of \r and \n to \r\n*/ +#define MODE_RAW 4 +/** Use non-secure mode (without SSL, only pure socket)*/ +#define MODE_NO_SSL 8 + +/*------ Functions ---------------*/ + +/** Return port inserted in string. Fuction tests, if port is integer, and than return + integer value of string. Otherwise, it will use /etc/services. On fail, it returns + port -1. + @param port_s Input port or service name + @return port number (converted with ntohs) on success, otherwise -1. +*/ +int return_port(char *port_s) { + char *end_c; + int res; + struct servent *serv; + + res=strtol(port_s,&end_c,10); + + if (*end_c=='\0') return res; + + /*It's not number, so try service name*/ + serv=getservbyname(port_s,NULL); + + if (serv==NULL) return -1; + + return ntohs(serv->s_port); +} + +/** Hook handler for bad certificate (because we have no DB, EVERY certificate is bad). + Returned value is always SECSuccess = it's ok certificate. + @param arg NULL value + @param fd socket cased error + @return SECSuccess. +*/ +SECStatus nss_bad_cert_hook(void *arg,PRFileDesc *fd) { + return SECSuccess; +} + +/** Display last NSPR/NSS error code and user readable message. +*/ +void print_nspr_error(void) { + fprintf(stderr,"Error (%d): %s\n",PR_GetError(),PR_ErrorToString(PR_GetError(),PR_LANGUAGE_I_DEFAULT)); +} + +/** Initialize NSS. NSS is initialized without DB and with + domnestic policy. + @return 1 on success, otherwise 0. +*/ +int init_nss(void) { + if ((NSS_NoDB_Init(NULL)!=SECSuccess) || + (NSS_SetDomesticPolicy()!=SECSuccess)) { + print_nspr_error(); + + return 0; + } + + SSL_ClearSessionCache(); + + return 1; +} + +/** Create socket. If ssl is >0, socket is ssl enabled. + @param ssl Enable ssl (Client, SSL2+3, no TLS, compatible hello) if PR_TRUE, otherwise no. + @param ipv6 New socket will be IPv4 if this value is 0, otherwise it will be ipv6 + @return NULL on error, otherwise socket. +*/ +PRFileDesc *create_socket(int ssl,int ipv6) { + PRFileDesc *res_socket; + + res_socket=PR_OpenTCPSocket((ipv6?PR_AF_INET6:PR_AF_INET)); + if (res_socket==NULL) { + print_nspr_error(); + + return NULL; + } + + if (!ssl) return res_socket; + + if (!(res_socket=SSL_ImportFD(NULL,res_socket))) { + print_nspr_error(); + + return NULL; + } + + if ((SSL_OptionSet(res_socket,SSL_SECURITY,ssl)!=SECSuccess) || + (SSL_OptionSet(res_socket,SSL_HANDSHAKE_AS_SERVER,PR_FALSE)!=SECSuccess) || + (SSL_OptionSet(res_socket,SSL_HANDSHAKE_AS_CLIENT,PR_TRUE)!=SECSuccess) || + (SSL_OptionSet(res_socket,SSL_ENABLE_SSL2,ssl)!=SECSuccess) || + (SSL_OptionSet(res_socket,SSL_ENABLE_SSL3,ssl)!=SECSuccess) || + (SSL_OptionSet(res_socket,SSL_ENABLE_TLS,PR_FALSE)!=SECSuccess) || + (SSL_OptionSet(res_socket,SSL_V2_COMPATIBLE_HELLO,ssl)!=SECSuccess) || + (SSL_SetPKCS11PinArg(res_socket,NULL)==-1) || + (SSL_AuthCertificateHook(res_socket,SSL_AuthCertificate,CERT_GetDefaultCertDB())!=SECSuccess) || + (SSL_BadCertHook(res_socket,nss_bad_cert_hook,NULL)!=SECSuccess)) { + print_nspr_error(); + + if (PR_Close(res_socket)!=PR_SUCCESS) { + print_nspr_error(); + } + + return NULL; + } + + return res_socket; +} + +/** Create socket and connect to it. + @param hostname Hostname to connect + @param port Port name/number to connect + @param mode Connection mode. Bit-array of MODE_NO_SSL, MODE_IP6MODE, MODE_IP4MODE. + @return NULL on error, otherwise connected socket. +*/ +PRFileDesc *create_connected_socket(char *hostname,int port,int mode) { + PRAddrInfo *addr_info; + void *addr_iter; + PRNetAddr addr; + PRFileDesc *socket; + int can_exit,valid_socket; + PRUint16 af_spec; + + socket=NULL; + + addr_info=NULL; + + af_spec=PR_AF_UNSPEC; + + if (!(mode&MODE_IP6MODE)) af_spec=PR_AF_INET; + + addr_info=PR_GetAddrInfoByName(hostname,af_spec,PR_AI_ADDRCONFIG); + + if (addr_info == NULL) { + print_nspr_error(); + return NULL; + } + + /*We have socket -> enumerate and try to connect*/ + addr_iter=NULL; + can_exit=0; + valid_socket=0; + + while (!can_exit) { + addr_iter=PR_EnumerateAddrInfo(addr_iter,addr_info,port,&addr); + + if (addr_iter==NULL) { + can_exit=1; + } else { + if ((PR_NetAddrFamily(&addr)==PR_AF_INET && (mode&MODE_IP4MODE)) || + (PR_NetAddrFamily(&addr)==PR_AF_INET6 && (mode&MODE_IP6MODE))) { + /*Type of address is what user want, try to create socket and make connection*/ + + /*Create socket*/ + socket=create_socket(!(mode&MODE_NO_SSL),(PR_NetAddrFamily(&addr)==PR_AF_INET6)); + + if (socket) { + /*Try to connect*/ + if (PR_Connect(socket,&addr,PR_INTERVAL_NO_TIMEOUT)==PR_SUCCESS) { + /*Force handshake*/ + if ((!(mode&MODE_NO_SSL)) && SSL_ForceHandshake(socket)!=SECSuccess) { + /*Handhake failure -> fail*/ + print_nspr_error(); + if (PR_Close(socket)!=PR_SUCCESS) { + print_nspr_error(); + can_exit=1; + } + socket=NULL; + } + + /*Socket is connected -> we can return it*/ + can_exit=1; + } else { + /*Try another address*/ + if (PR_Close(socket)!=PR_SUCCESS) { + print_nspr_error(); + can_exit=1; + } + socket=NULL; + } + } + } + } + } + + if (!socket) { + /*Socket is unvalid -> we don't found any usable address*/ + fprintf(stderr,"Can't connect to host %s on port %d!\n",hostname,port); + } + + PR_FreeAddrInfo(addr_info); + + return socket; +} + +/** Parse arguments from command line. + @param argc Number of arguments in argv + @param argv Array of arguments + @param mode Pointer to int will be filled with OPERATION_DEFAULT or OPERATION_HELP. + @param mode Pointer to int will be filled with MODE_DEFAULT, MODE_IP4MODE or MODE_IP4MODE. + @return 1 on success, otherwise 0. +*/ +int parse_cli(int argc,char *argv[],int *operation,int *mode,char **hostname,char **port) { + int opt; + + *operation=OPERATION_DEFAULT; + *mode=MODE_DEFAULT; + *port=NULL; + *hostname=NULL; + + while ((opt=getopt(argc,argv,"h46rz"))!=-1) { + switch (opt) { + case 'h': + *operation=OPERATION_HELP; + + return 0; + break; + + case '4': + (*mode)&=~MODE_IP6MODE; + (*mode)|=MODE_IP4MODE; + break; + + case '6': + (*mode)&=~MODE_IP4MODE; + (*mode)|=MODE_IP6MODE; + break; + + case 'r': + (*mode)|=MODE_RAW; + break; + + case 'z': + (*mode)|=MODE_NO_SSL; + break; + + default: + return 0; + break; + } + } + + if (argc-optind<2) { + fprintf(stderr,"Hostname and port is expected!\n"); + + return 0; + } + + *hostname=argv[optind]; + *port=argv[optind+1]; + + return 1; +} + +/** Show usage of application. + @param pname Name of program (usually basename of argv[0]) +*/ +void show_usage(char *pname) { + printf("usage: %s [options] hostname port\n", pname); + printf(" -4 Force to use IPv4\n"); + printf(" -6 Force to use IPv6\n"); + printf(" -r Use RAW connection (don't convert \\r and \\n characters)\n"); + printf(" -z Don't use SSL connection (use pure socket)\n"); + printf(" -h Show this help\n"); +} + +/** Convert End Of Lines (Unix \n, Macs \r or DOS/Win \r\n) to \r\n. + @param in_buffer Input buffer + @param in_size Input buffer size + @param out_buffer Output buffer (must be prealocated). Should be (2*in_size) (in worst case) + @param out_size There will be size of out_buffer + @param in_state Internal state of finite automata. First call should have this 0, other calls + shouldn't change this value. After end of file, you may add to this value +100 and call this + again, to make sure of proper end (in_buffer can be in this case everything, including NULL). +*/ +void convert_eols(char *in_buffer,int in_size,char *out_buffer,int *out_size,int *in_state) { + int in_pos,out_pos; + int status; + char in_char; + + out_pos=0; + status=*in_state; + + if (status==100 || status==101) { + if (status==101) { + out_buffer[out_pos++]='\r'; + out_buffer[out_pos++]='\n'; + } + } else { + for (in_pos=0;in_pos0) { + if (PR_Write(PR_STDOUT,buffer,readed_bytes)!=readed_bytes) { + print_nspr_error(); + + return 0; + } + } else { + /*End of stream -> quit*/ + can_exit=1; + } + } + + if (pool[0].out_flags==PR_POLL_READ) { + /*We have something in stdin*/ + if ((readed_bytes=PR_Read(pool[0].fd,buffer,sizeof(buffer)))>0) { + + if (!(mode&MODE_RAW)) { + convert_eols(buffer,readed_bytes,buffer_eol,&bytes_to_write,&eol_state); + } else + bytes_to_write=readed_bytes; + + if (PR_Write(pool[1].fd,(mode&MODE_RAW?buffer:buffer_eol),bytes_to_write)!=bytes_to_write) { + print_nspr_error(); + + return 0; + } + } else { + /*End of stream -> quit*/ + if (!(mode&MODE_RAW)) { + convert_eols(NULL,0,buffer_eol,&bytes_to_write,&eol_state); + if (PR_Write(pool[1].fd,buffer_eol,bytes_to_write)!=bytes_to_write) { + print_nspr_error(); + + return 0; + } + } + + can_exit=1; + } + } + + pool[0].out_flags=pool[1].out_flags=0; + } /*while (!can_exit)*/ + + return 1; +} + +void atexit_handler(void) { + if (PR_Initialized()) + PR_Cleanup(); + + if (fclose(stdout)!=0) { + fprintf(stderr,"Can't close stdout!\n"); + + exit(1); + } +} + +/** Entry point of application. + @param argc Number of arguments on command line + @param argv Array of strings with arguments from command line + @return 0 on success, otherwise >0. +*/ +int main(int argc,char *argv[]) { + int mode,operation; + char *hostname, *port; + char *pname; + int port_n; + PRFileDesc *fd_socket; + int res; + + pname=basename(argv[0]); + + atexit(atexit_handler); + + if (!parse_cli(argc,argv,&operation,&mode,&hostname,&port) || operation==OPERATION_HELP) { + show_usage(pname); + + if (operation!=OPERATION_HELP) return 1; + + return 0; + } + + if ((port_n=return_port(port))==-1) { + fprintf(stderr,"Error. Unknown port number/name %s!\n",port); + + return 1; + } + + if (!(mode&MODE_NO_SSL)) { + if (!init_nss()) return 1; + } + + if (!(fd_socket=create_connected_socket(hostname,port_n,mode))) + return 1; + + res=poll_cycle(fd_socket,mode); + + if (PR_Close(fd_socket)!=PR_SUCCESS) { + print_nspr_error(); + + return 1; + } + + return (res?0:1); +} diff --git a/make/fencebuild.mk b/make/fencebuild.mk index 7c5f0c5..48145b7 100644 --- a/make/fencebuild.mk +++ b/make/fencebuild.mk @@ -19,6 +19,7 @@ $(TARGET): -e 's#@FENCEAGENTSLIBDIR@#${fenceagentslibdir}#g' \ -e 's#@SNMPBIN@#${snmpbin}#g' \ -e 's#@LOGDIR@#${logdir}#g' \ + -e 's#@SBINDIR@#${sbindir}#g' \ > $@ clean: generalclean