1/*
2 * Copyright (c) 2006-2008,2010-2011,2013 Apple Inc. All Rights Reserved.
3 *
4 * sslClient.cpp : perform one SSL client side sesssion
5 */
6
7#include <Security/SecureTransport.h>
8#include <Security/Security.h>
9#include <clAppUtils/sslAppUtils.h>
10#include <clAppUtils/ioSock.h>
11#include <clAppUtils/sslThreading.h>
12#include <utilLib/fileIo.h>
13#include <utilLib/common.h>
14#include <security_cdsa_utils/cuPrintCert.h>
15
16#include <Security/SecBase.h>
17
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <time.h>
22#include <ctype.h>
23#include <sys/param.h>
24
25/* when true, keep listening until server disconnects */
26#define KEEP_CONNECTED	1
27
28#define CLIENT_GETMSG  	"GET / HTTP/1.0\r\n\r\n"
29
30#define READBUF_LEN		256
31
32/* relies on SSLSetProtocolVersionEnabled */
33OSStatus sslAppClient(
34	SslAppTestParams	*params)
35{
36    PeerSpec            peerId;
37	otSocket			sock = 0;
38    OSStatus            ortn;
39    SSLContextRef       ctx = NULL;
40	SecKeychainRef		clientKc = nil;
41	CFArrayRef			clientCerts = nil;
42
43	sslThrDebug("Client", "starting");
44    params->negVersion = kSSLProtocolUnknown;
45    params->negCipher  = SSL_NULL_WITH_NULL_NULL;
46    params->ortn       = noHardwareErr;
47
48	/* first make sure requested server is there */
49	ortn = MakeServerConnection(params->hostName, params->port,
50		params->nonBlocking, &sock, &peerId);
51    if(ortn) {
52    	printf("MakeServerConnection returned %d; aborting\n", (int)ortn);
53    	return ortn;
54    }
55
56	/*
57	 * Set up a SecureTransport session.
58	 */
59	ortn = SSLNewContext(false, &ctx);
60	if(ortn) {
61		printSslErrStr("SSLNewContext", ortn);
62		goto cleanup;
63	}
64	ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite);
65	if(ortn) {
66		printSslErrStr("SSLSetIOFuncs", ortn);
67		goto cleanup;
68	}
69	ortn = SSLSetConnection(ctx, (SSLConnectionRef)sock);
70	if(ortn) {
71		printSslErrStr("SSLSetConnection", ortn);
72		goto cleanup;
73	}
74	if(!params->skipHostNameCheck) {
75		ortn = SSLSetPeerDomainName(ctx, params->hostName,
76			strlen(params->hostName));
77		if(ortn) {
78			printSslErrStr("SSLSetPeerDomainName", ortn);
79			goto cleanup;
80		}
81	}
82
83	/* remainder of setup is optional */
84	if(params->anchorFile) {
85		ortn = sslAddTrustedRoot(ctx, params->anchorFile, params->replaceAnchors);
86		if(ortn) {
87			goto cleanup;
88		}
89	}
90	ortn = sslSetProtocols(ctx, params->acceptedProts, params->tryVersion);
91	if(ortn) {
92		goto cleanup;
93	}
94	if(params->resumeEnable) {
95		ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec));
96		if(ortn) {
97			printSslErrStr("SSLSetPeerID", ortn);
98			goto cleanup;
99		}
100	}
101	if(params->disableCertVerify) {
102		ortn = SSLSetEnableCertVerify(ctx, false);
103		if(ortn) {
104			printSslErrStr("SSLSetEnableCertVerify", ortn);
105			goto cleanup;
106		}
107	}
108	if(params->ciphers != NULL) {
109		ortn = sslSetEnabledCiphers(ctx, params->ciphers);
110		if(ortn) {
111			goto cleanup;
112		}
113	}
114	if(params->myCertKcName) {
115		clientCerts = getSslCerts(params->myCertKcName, false, false, NULL, &clientKc);
116		if(clientCerts == nil) {
117			exit(1);
118		}
119		if(params->password) {
120			ortn = SecKeychainUnlock(clientKc, strlen(params->password),
121					(void *)params->password, true);
122			if(ortn) {
123				printf("SecKeychainUnlock returned %d\n", (int)ortn);
124				/* oh well */
125			}
126		}
127		if(params->idIsTrustedRoot) {
128			/* assume this is a root we want to implicitly trust */
129			ortn = addIdentityAsTrustedRoot(ctx, clientCerts);
130			if(ortn) {
131				goto cleanup;
132			}
133		}
134		ortn = SSLSetCertificate(ctx, clientCerts);
135		if(ortn) {
136			printSslErrStr("SSLSetCertificate", ortn);
137			goto cleanup;
138		}
139	}
140    do {
141		ortn = SSLHandshake(ctx);
142	    if((ortn == errSSLWouldBlock) && !params->silent) {
143	    	/* keep UI responsive */
144	    	sslOutputDot();
145	    }
146    } while (ortn == errSSLWouldBlock);
147
148	SSLGetClientCertificateState(ctx, &params->certState);
149	SSLGetNegotiatedCipher(ctx, &params->negCipher);
150	SSLGetNegotiatedProtocolVersion(ctx, &params->negVersion);
151
152	if(ortn != errSecSuccess) {
153		goto cleanup;
154	}
155
156	/* send a GET msg */
157	size_t actLen;
158	ortn = SSLWrite(ctx, CLIENT_GETMSG, strlen(CLIENT_GETMSG), &actLen);
159	if(ortn) {
160		printSslErrStr("SSLWrite", ortn);
161		goto cleanup;
162	}
163
164	#if KEEP_CONNECTED
165
166	/*
167	 * Consume any server data and wait for server to disconnect
168	 */
169	char readBuf[READBUF_LEN];
170    do {
171		ortn = SSLRead(ctx, readBuf, READBUF_LEN, &actLen);
172    } while (ortn == errSSLWouldBlock);
173
174    /* convert normal "shutdown" into zero err rtn */
175	if(ortn == errSSLClosedGraceful) {
176		ortn = errSecSuccess;
177	}
178	#endif	/* KEEP_CONNECTED */
179
180cleanup:
181	if(ctx) {
182		OSStatus cerr = SSLClose(ctx);
183		if(ortn == errSecSuccess) {
184			ortn = cerr;
185		}
186	}
187	if(sock) {
188		endpointShutdown(sock);
189	}
190	if(ctx) {
191	    SSLDisposeContext(ctx);
192	}
193	params->ortn = ortn;
194	sslThrDebug("Client", "done");
195	return ortn;
196}
197