Exemple #1
0
static DWORD schan_get_session_protocol(struct mac_session* s)
{
    SSLProtocol protocol;
    int status;

    TRACE("(%p/%p)\n", s, s->context);

    status = SSLGetNegotiatedProtocolVersion(s->context, &protocol);
    if (status != noErr)
    {
        ERR("Failed to get session protocol: %d\n", status);
        return 0;
    }

    TRACE("protocol %d\n", protocol);

    switch (protocol)
    {
    case kSSLProtocol2:     return SP_PROT_SSL2_CLIENT;
    case kSSLProtocol3:     return SP_PROT_SSL3_CLIENT;
    case kTLSProtocol1:     return SP_PROT_TLS1_CLIENT;
    case kTLSProtocol11:    return SP_PROT_TLS1_1_CLIENT;
    case kTLSProtocol12:    return SP_PROT_TLS1_2_CLIENT;
    default:
        FIXME("unknown protocol %d\n", protocol);
        return 0;
    }
}
Exemple #2
0
static gboolean get_cipher_details(PurpleSslConnection *gsc /* IN */, const char **ssl_info /* OUT */, const char **name /* OUT */, const char **mac /* OUT */, const char **key_exchange /* OUT */) {
	PurpleSslCDSAData *cdsa_data = PURPLE_SSL_CDSA_DATA(gsc);
	OSStatus err;
	SSLCipherSuite cipher;
	SSLProtocol protocol;
	
	err = SSLGetNegotiatedCipher(cdsa_data->ssl_ctx, &cipher);
	
	if (err != noErr) return FALSE;
	
	err = SSLGetNegotiatedProtocolVersion(cdsa_data->ssl_ctx, &protocol);
	
	if (err != noErr) return FALSE;
	
	*ssl_info = SSLVersionToString(protocol);
	*name = SSLCipherName(cipher);
	*mac = SSLMACName(cipher);
	*key_exchange = SSLKeyExchangeName(cipher);
	
	return TRUE;
}
Exemple #3
0
static CURLcode
darwinssl_connect_step2(struct connectdata *conn, int sockindex)
{
  struct SessionHandle *data = conn->data;
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  OSStatus err;
  SSLCipherSuite cipher;
  SSLProtocol protocol = 0;

  DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
              || ssl_connect_2_reading == connssl->connecting_state
              || ssl_connect_2_writing == connssl->connecting_state);

  /* Here goes nothing: */
  err = SSLHandshake(connssl->ssl_ctx);

  if(err != noErr) {
    switch (err) {
      case errSSLWouldBlock:  /* they're not done with us yet */
        connssl->connecting_state = connssl->ssl_direction ?
            ssl_connect_2_writing : ssl_connect_2_reading;
        return CURLE_OK;
        break;

      case errSSLServerAuthCompleted:
        /* the documentation says we need to call SSLHandshake() again */
        return darwinssl_connect_step2(conn, sockindex);

      case errSSLXCertChainInvalid:
      case errSSLUnknownRootCert:
      case errSSLNoRootCert:
      case errSSLCertExpired:
        failf(data, "SSL certificate problem: OSStatus %d", err);
        return CURLE_SSL_CACERT;
        break;

      default:
        failf(data, "Unknown SSL protocol error in connection to %s:%d",
              conn->host.name, err);
        return CURLE_SSL_CONNECT_ERROR;
        break;
    }
  }
  else {
    /* we have been connected fine, we're not waiting for anything else. */
    connssl->connecting_state = ssl_connect_3;

    /* Informational message */
    (void)SSLGetNegotiatedCipher(connssl->ssl_ctx, &cipher);
    (void)SSLGetNegotiatedProtocolVersion(connssl->ssl_ctx, &protocol);
    switch (protocol) {
      case kSSLProtocol2:
        infof(data, "SSL 2.0 connection using %s\n",
              SSLCipherNameForNumber(cipher));
        break;
      case kSSLProtocol3:
        infof(data, "SSL 3.0 connection using %s\n",
              SSLCipherNameForNumber(cipher));
        break;
      case kTLSProtocol1:
        infof(data, "TLS 1.0 connection using %s\n",
              TLSCipherNameForNumber(cipher));
        break;
#if defined(__MAC_10_8) || defined(__IPHONE_5_0)
      case kTLSProtocol11:
        infof(data, "TLS 1.1 connection using %s\n",
              TLSCipherNameForNumber(cipher));
        break;
      case kTLSProtocol12:
        infof(data, "TLS 1.2 connection using %s\n",
              TLSCipherNameForNumber(cipher));
        break;
#endif
      default:
        infof(data, "Unknown protocol connection\n");
        break;
    }

    return CURLE_OK;
  }
}
Exemple #4
0
/*
 * ssl_cdsa_handshake_cb
 */
static void
ssl_cdsa_handshake_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	PurpleSslConnection *gsc = (PurpleSslConnection *)data;
	PurpleAccount *account = gsc->account;
	PurpleSslCDSAData *cdsa_data = PURPLE_SSL_CDSA_DATA(gsc);
    OSStatus err;
	
	purple_debug_info("cdsa", "Connecting\n");
	
	/*
	 * do the negotiation that sets up the SSL connection between
	 * here and there.
	 */
	err = SSLHandshake(cdsa_data->ssl_ctx);
    if (err == errSSLPeerBadRecordMac
		&& !purple_account_get_bool(account, PURPLE_SSL_CDSA_BUGGY_TLS_WORKAROUND, false)
		&& !strcmp(purple_account_get_protocol_id(account),"prpl-jabber")) {
        /*
         * Set a flag so we know to explicitly disable TLS 1.1 and 1.2 on our next (immediate) connection attempt for this account.
         * Some XMPP servers use buggy TLS stacks that incorrectly report their capabilities, which breaks things with 10.8's new support
         * for TLS 1.1 and 1.2.
         */
        purple_debug_info("cdsa", "SSLHandshake reported that the server rejected our MAC, which most likely means it lied about the TLS versions it supports.");
        purple_debug_info("cdsa", "Setting a flag in this account to only use TLS 1.0 and below on the next connection attempt.");
    
        purple_account_set_bool(account, PURPLE_SSL_CDSA_BUGGY_TLS_WORKAROUND, true);
        if (gsc->error_cb != NULL)
            gsc->error_cb(gsc, PURPLE_SSL_HANDSHAKE_FAILED, gsc->connect_cb_data);
        purple_ssl_close(gsc);
        return;
    } else if (err != noErr) {
		if(err == errSSLWouldBlock)
			return;
		fprintf(stderr,"cdsa: SSLHandshake failed with error %d\n",(int)err);
		purple_debug_error("cdsa", "SSLHandshake failed with error %d\n",(int)err);
		if (gsc->error_cb != NULL)
			gsc->error_cb(gsc, PURPLE_SSL_HANDSHAKE_FAILED,
						  gsc->connect_cb_data);
		
		purple_ssl_close(gsc);
		return;
	}
	
	SSLCipherSuite cipher;
	SSLProtocol protocol;
	
	err = SSLGetNegotiatedCipher(cdsa_data->ssl_ctx, &cipher);
	
	if (err == noErr) {
		err = SSLGetNegotiatedProtocolVersion(cdsa_data->ssl_ctx, &protocol);
		
		purple_debug_info("cdsa", "Your connection is using %s with %s encryption, using %s for message authentication and %s key exchange (%X).\n",
						  SSLVersionToString(protocol),
						  SSLCipherName(cipher),
						  SSLMACName(cipher),
						  SSLKeyExchangeName(cipher),
						  cipher);
	}
	
	purple_input_remove(cdsa_data->handshake_handler);
	cdsa_data->handshake_handler = 0;
	
	purple_debug_info("cdsa", "SSL_connect: verifying certificate\n");
	
	if(certificate_ui_cb) { // does the application want to verify the certificate?
		struct query_cert_userdata *userdata = (struct query_cert_userdata*)malloc(sizeof(struct query_cert_userdata));
		size_t hostnamelen = 0;
		
		SSLGetPeerDomainNameLength(cdsa_data->ssl_ctx, &hostnamelen);
		userdata->hostname = (char*)malloc(hostnamelen+1);
		SSLGetPeerDomainName(cdsa_data->ssl_ctx, userdata->hostname, &hostnamelen);
		userdata->hostname[hostnamelen] = '\0'; // just make sure it's zero-terminated
		userdata->cond = cond;
		userdata->gsc = gsc;
		SSLCopyPeerCertificates(cdsa_data->ssl_ctx, &userdata->certs);
		
		certificate_ui_cb(gsc, userdata->hostname, userdata->certs, query_cert_result, userdata);
	} else {
		purple_debug_info("cdsa", "SSL_connect complete (did not verify certificate)\n");
		
		/* SSL connected now */
		gsc->connect_cb(gsc->connect_cb_data, gsc, cond);
	}
	
	SSLCipherSuite suite;
	SSLGetNegotiatedCipher(cdsa_data->ssl_ctx, &suite);
	
	purple_debug_info("cdsa", "Using cipher %x.\n", suite);
}
static OSStatus sslServe(
	otSocket				listenSock,
	unsigned short			portNum,
	SSLProtocol				tryVersion,			// only used if acceptedProts NULL
	const char				*acceptedProts,
	CFArrayRef				serverCerts,		// required
	char					*password,			// optional
	CFArrayRef				encryptServerCerts,	// optional
	bool				allowExpired,
	bool				allowAnyRoot,
	bool				allowExpiredRoot,
	bool				disableCertVerify,
	char					*anchorFile,
	bool				replaceAnchors,
	char					cipherRestrict,		// '2', 'd'. etc...'\0' for no
												//   restriction
	SSLAuthenticate			authenticate,
	unsigned char			*dhParams,			// optional D-H parameters	
	unsigned				dhParamsLen,
	CFArrayRef				acceptableDNList,	// optional 
	bool				resumableEnable,
	uint32_t					sessionCacheTimeout,// optional
	bool				disableAnonCiphers,
	bool				silent,				// no stdout
	bool				pause,
	SSLProtocol				*negVersion,		// RETURNED
	SSLCipherSuite			*negCipher,			// RETURNED
	SSLClientCertificateState *certState,		// RETURNED
	Boolean					*sessionWasResumed,	// RETURNED
	unsigned char			*sessionID,			// mallocd by caller, RETURNED
	size_t					*sessionIDLength,	// RETURNED
	CFArrayRef				*peerCerts,			// mallocd & RETURNED
	char					**argv)
{
	otSocket			acceptSock;
    PeerSpec            peerId;
    OSStatus            ortn;
    SSLContextRef       ctx = NULL;
    size_t              length;
    uint8_t             rcvBuf[RCV_BUF_SIZE];
	const char *outMsg = SERVER_MESSAGE;
	
    *negVersion = kSSLProtocolUnknown;
    *negCipher = SSL_NULL_WITH_NULL_NULL;
    *peerCerts = NULL;
    
	#if IGNORE_SIGPIPE
	signal(SIGPIPE, sigpipe);
	#endif
	
	/* first wait for a connection */
	if(!silent) {
		printf("Waiting for client connection on port %u...", portNum);
		fflush(stdout);
	}
	ortn = AcceptClientConnection(listenSock, &acceptSock, &peerId);
    if(ortn) {
    	printf("AcceptClientConnection returned %d; aborting\n", (int)ortn);
    	return ortn;
    }

	/* 
	 * Set up a SecureTransport session.
	 * First the standard calls.
	 */
	ortn = SSLNewContext(true, &ctx);
	if(ortn) {
		printSslErrStr("SSLNewContext", ortn);
		goto cleanup;
	} 
	ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite);
	if(ortn) {
		printSslErrStr("SSLSetIOFuncs", ortn);
		goto cleanup;
	} 
	ortn = SSLSetConnection(ctx, (SSLConnectionRef)(intptr_t)acceptSock);
	if(ortn) {
		printSslErrStr("SSLSetConnection", ortn);
		goto cleanup;
	}
	
	/* have to do these options befor setting server certs */
	if(allowExpired) {
		ortn = SSLSetAllowsExpiredCerts(ctx, true);
		if(ortn) {
			printSslErrStr("SSLSetAllowExpiredCerts", ortn);
			goto cleanup;
		}
	}
	if(allowAnyRoot) {
		ortn = SSLSetAllowsAnyRoot(ctx, true);
		if(ortn) {
			printSslErrStr("SSLSetAllowAnyRoot", ortn);
			goto cleanup;
		}
	}

	if(anchorFile) {
		ortn = sslAddTrustedRoot(ctx, anchorFile, replaceAnchors);
		if(ortn) {
			printf("***Error obtaining anchor file %s\n", anchorFile);
			goto cleanup;
		}
	}
	if(serverCerts != NULL) {
		if(anchorFile == NULL) {
			/* no specific anchors, so assume we want to trust this one */
			ortn = addIdentityAsTrustedRoot(ctx, serverCerts);
			if(ortn) {
				goto cleanup;
			}
		}
		ortn = SSLSetCertificate(ctx, serverCerts);
		if(ortn) {
			printSslErrStr("SSLSetCertificate", ortn);
			goto cleanup;
		}
	}
	if(encryptServerCerts) {
		ortn = SSLSetEncryptionCertificate(ctx, encryptServerCerts);
		if(ortn) {
			printSslErrStr("SSLSetEncryptionCertificate", ortn);
			goto cleanup;
		}
	}
	if(allowExpiredRoot) {
		ortn = SSLSetAllowsExpiredRoots(ctx, true);
		if(ortn) {
			printSslErrStr("SSLSetAllowsExpiredRoots", ortn);
			goto cleanup;
		}
	}
	if(disableCertVerify) {
		ortn = SSLSetEnableCertVerify(ctx, false);
		if(ortn) {
			printSslErrStr("SSLSetEnableCertVerify", ortn);
			goto cleanup;
		}
	}
	
	/* 
	 * SecureTransport options.
	 */ 
	if(acceptedProts) {
		ortn = SSLSetProtocolVersionEnabled(ctx, kSSLProtocolAll, false);
		if(ortn) {
			printSslErrStr("SSLSetProtocolVersionEnabled(all off)", ortn);
			goto cleanup;
		}
		for(const char *cp = acceptedProts; *cp; cp++) {
			SSLProtocol prot = kSSLProtocolUnknown;
			switch(*cp) {
				case '2':
					prot = kSSLProtocol2;
					break;
				case '3':
					prot = kSSLProtocol3;
					break;
				case 't':
					prot = kTLSProtocol1;
					break;
				default:
					usage(argv);
			}
			ortn = SSLSetProtocolVersionEnabled(ctx, prot, true);
			if(ortn) {
				printSslErrStr("SSLSetProtocolVersionEnabled", ortn);
				goto cleanup;
			}
		}
	}
	else {
		ortn = SSLSetProtocolVersion(ctx, tryVersion);
		if(ortn) {
			printSslErrStr("SSLSetProtocolVersion", ortn);
			goto cleanup;
		} 
	}
	if(resumableEnable) {
		ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec));
		if(ortn) {
			printSslErrStr("SSLSetPeerID", ortn);
			goto cleanup;
		}
	}
	if(cipherRestrict != '\0') {
		ortn = sslSetCipherRestrictions(ctx, cipherRestrict);
		if(ortn) {
			goto cleanup;
		}
	}
	if(authenticate != kNeverAuthenticate) {
		ortn = SSLSetClientSideAuthenticate(ctx, authenticate);
		if(ortn) {
			printSslErrStr("SSLSetClientSideAuthenticate", ortn);
			goto cleanup;
		}
	}
	if(dhParams) {
		ortn = SSLSetDiffieHellmanParams(ctx, dhParams, dhParamsLen);
		if(ortn) {
			printSslErrStr("SSLSetDiffieHellmanParams", ortn);
			goto cleanup;
		}
	}
	if(sessionCacheTimeout) {
		ortn = SSLSetSessionCacheTimeout(ctx, sessionCacheTimeout);
		if(ortn) {
			printSslErrStr("SSLSetSessionCacheTimeout", ortn);
			goto cleanup;
		}
	}
	if(disableAnonCiphers) {
		ortn = SSLSetAllowAnonymousCiphers(ctx, false);
		if(ortn) {
			printSslErrStr("SSLSetAllowAnonymousCiphers", ortn);
			goto cleanup;
		}
		/* quickie test of the getter */
		Boolean e;
		ortn = SSLGetAllowAnonymousCiphers(ctx, &e);
		if(ortn) {
			printSslErrStr("SSLGetAllowAnonymousCiphers", ortn);
			goto cleanup;
		}
		if(e) {
			printf("***SSLGetAllowAnonymousCiphers() returned true; expected false\n");
			ortn = errSecIO;
			goto cleanup;
		}
	}
/* XXX/cs
	if(acceptableDNList) {
		ortn = SSLSetCertificateAuthorities(ctx, acceptableDNList, TRUE);
		if(ortn) {
			printSslErrStr("SSLSetCertificateAuthorities", ortn);
			goto cleanup;
		}
	}
*/
	/* end options */

	if(pause) {
		doPause("SSLContext initialized");
	}
	
	/* Perform SSL/TLS handshake */
    do
    {   ortn = SSLHandshake(ctx);
	    if((ortn == errSSLWouldBlock) && !silent) {
	    	/* keep UI responsive */ 
	    	sslOutputDot();
	    }
    } while (ortn == errSSLWouldBlock);
	
	/* this works even if handshake failed due to cert chain invalid */
	copyPeerCerts(ctx, peerCerts);

	SSLGetClientCertificateState(ctx, certState);
	SSLGetNegotiatedCipher(ctx, negCipher);
	SSLGetNegotiatedProtocolVersion(ctx, negVersion);
	*sessionIDLength = MAX_SESSION_ID_LENGTH;
	SSLGetResumableSessionInfo(ctx, sessionWasResumed, sessionID,
		sessionIDLength);
	
	if(!silent) {
		printf("\n");
	}
    if(ortn) {
    	goto cleanup;
    }
	if(pause) {
		doPause("SSLContext handshake complete");
	}

	/* wait for one complete line or user says they've had enough */
	while(ortn == errSecSuccess) {
	    length = sizeof(rcvBuf);
	    ortn = SSLRead(ctx, rcvBuf, length, &length);
	    if(length == 0) {
	    	/* keep UI responsive */ 
	    	sslOutputDot();
	    }
	    else {
	    	/* print what we have */
	    	printf("client request: ");
	    	dumpAscii(rcvBuf, length);
	    }
	    if(pause) {
	    	/* allow user to bail */
	    	char resp;
	    	
			fpurge(stdin);
	    	printf("\nMore client request (y/anything): ");
	    	resp = getchar();
	    	if(resp != 'y') {
	    		break;
	    	}
	    }
	    
	    /* poor person's line completion scan */
	    for(unsigned i=0; i<length; i++) {
	    	if((rcvBuf[i] == '\n') || (rcvBuf[i] == '\r')) {
	    		/* a labelled break would be nice here.... */
	    		goto serverResp;
	    	}
	    }
	    if (ortn == errSSLWouldBlock) {
	        ortn = errSecSuccess;
	    }
	}
	
serverResp:
	if(pause) {
		doPause("Client GET msg received");
	}

	/* send out canned response */
	length = strlen(outMsg);
 	ortn = SSLWrite(ctx, outMsg, length, &length);
 	if(ortn) {
 		printSslErrStr("SSLWrite", ortn);
 	}
	if(pause) {
		doPause("Server response sent");
	}
cleanup:
	/*
	 * always do close, even on error - to flush outgoing write queue 
	 */
	OSStatus cerr = SSLClose(ctx);
	if(ortn == errSecSuccess) {
		ortn = cerr;
	}
	if(acceptSock) {
		endpointShutdown(acceptSock);
	}
	if(ctx) {
	    SSLDisposeContext(ctx);  
	}    
	/* FIXME - dispose of serverCerts */
	return ortn;
}
Exemple #6
0
static OSStatus sslServe(
	otSocket				listenSock,
	SSLProtocol				tryVersion,
	const char				*hostName,			// e.g., "www.amazon.com"
	CFArrayRef				serverCerts,		// required
	CFArrayRef				encryptServerCerts,	// optional
	CSSM_BOOL				allowExpired,
	CSSM_BOOL				allowAnyRoot,
	char					cipherRestrict,		// '2', 'd'. etc...'\0' for no
												//   restriction
	SSLAuthenticate			authenticate,
	CSSM_BOOL				resumableEnable,
	CSSM_BOOL				silent,				// no stdout
	CSSM_BOOL				pause,
	SSLProtocol				*negVersion,		// RETURNED
	SSLCipherSuite			*negCipher,			// RETURNED
	CFArrayRef				*peerCerts)			// mallocd & RETURNED
{
	otSocket			acceptSock;
    PeerSpec            peerId;
    OSStatus            ortn;
    SSLContextRef       ctx = NULL;
    UInt32              length;
    uint8               rcvBuf[RCV_BUF_SIZE];
	char *outMsg = SERVER_MESSAGE;
	
    *negVersion = kSSLProtocolUnknown;
    *negCipher = SSL_NULL_WITH_NULL_NULL;
    *peerCerts = NULL;
    
	#if IGNORE_SIGPIPE
	signal(SIGPIPE, sigpipe);
	#endif
	
	/* first wait for a connection */
	if(!silent) {
		printf("Waiting for client connection...");
		fflush(stdout);
	}
	ortn = AcceptClientConnection(listenSock, &acceptSock, &peerId);
    if(ortn) {
    	printf("AcceptClientConnection returned %d; aborting\n", ortn);
    	return ortn;
    }

	/* 
	 * Set up a SecureTransport session.
	 * First the standard calls.
	 */
	ortn = SSLNewContext(true, &ctx);
	if(ortn) {
		printSslErrStr("SSLNewContext", ortn);
		goto cleanup;
	} 
	ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite);
	if(ortn) {
		printSslErrStr("SSLSetIOFuncs", ortn);
		goto cleanup;
	} 
	ortn = SSLSetProtocolVersion(ctx, tryVersion);
	if(ortn) {
		printSslErrStr("SSLSetProtocolVersion", ortn);
		goto cleanup;
	} 
	ortn = SSLSetConnection(ctx, acceptSock);
	if(ortn) {
		printSslErrStr("SSLSetConnection", ortn);
		goto cleanup;
	}
	ortn = SSLSetPeerDomainName(ctx, hostName, strlen(hostName) + 1);
	if(ortn) {
		printSslErrStr("SSLSetPeerDomainName", ortn);
		goto cleanup;
	}
	
	/* have to do these options befor setting server certs */
	if(allowExpired) {
		ortn = SSLSetAllowsExpiredCerts(ctx, true);
		if(ortn) {
			printSslErrStr("SSLSetAllowExpiredCerts", ortn);
			goto cleanup;
		}
	}
	if(allowAnyRoot) {
		ortn = SSLSetAllowsAnyRoot(ctx, true);
		if(ortn) {
			printSslErrStr("SSLSetAllowAnyRoot", ortn);
			goto cleanup;
		}
	}

	ortn = SSLSetCertificate(ctx, serverCerts);
	if(ortn) {
		printSslErrStr("SSLSetCertificate", ortn);
		goto cleanup;
	}
	if(encryptServerCerts) {
		ortn = SSLSetEncryptionCertificate(ctx, encryptServerCerts);
		if(ortn) {
			printSslErrStr("SSLSetEncryptionCertificate", ortn);
			goto cleanup;
		}
	}
	
	/* 
	 * SecureTransport options.
	 */ 
	if(resumableEnable) {
		ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec));
		if(ortn) {
			printSslErrStr("SSLSetPeerID", ortn);
			goto cleanup;
		}
	}
	if(cipherRestrict != '\0') {
		ortn = setCipherRestrictions(ctx, cipherRestrict);
		if(ortn) {
			goto cleanup;
		}
	}
	#if AUTHENTICATE_ENABLE
	if(authenticate != kNeverAuthenticate) {
		ortn = SSLSetClientSideAuthenticate(ctx, authenticate);
		if(ortn) {
			printSslErrStr("SSLSetClientSideAuthenticate", ortn);
			goto cleanup;
		}
	}
	#endif
	if(pause) {
		doPause("SSLContext initialized");
	}
	
	/* Perform SSL/TLS handshake */
    do
    {   ortn = SSLHandshake(ctx);
	    if((ortn == errSSLWouldBlock) && !silent) {
	    	/* keep UI responsive */ 
	    	outputDot();
	    }
    } while (ortn == errSSLWouldBlock);
	
	/* this works even if handshake failed due to cert chain invalid */
	copyPeerCerts(ctx, peerCerts);

	SSLGetNegotiatedCipher(ctx, negCipher);
	SSLGetNegotiatedProtocolVersion(ctx, negVersion);
	
	if(!silent) {
		printf("\n");
	}
    if(ortn) {
    	goto cleanup;
    }
	if(pause) {
		doPause("SSLContext handshake complete");
	}

	/* wait for one complete line or user says they've had enough */
	while(ortn == noErr) {
	    length = sizeof(rcvBuf);
	    ortn = SSLRead(ctx, rcvBuf, length, &length);
	    if(length == 0) {
	    	/* keep UI responsive */ 
	    	outputDot();
	    }
	    else {
	    	/* print what we have */
	    	printf("client request: ");
	    	dumpAscii(rcvBuf, length);
	    }
	    if(pause) {
	    	/* allow user to bail */
	    	char resp;
	    	
			fpurge(stdin);
	    	printf("\nMore client request (y/anything): ");
	    	resp = getchar();
	    	if(resp != 'y') {
	    		break;
	    	}
	    }
	    
	    /* poor person's line completion scan */
	    for(unsigned i=0; i<length; i++) {
	    	if((rcvBuf[i] == '\n') || (rcvBuf[i] == '\r')) {
	    		/* a labelled break would be nice here.... */
	    		goto serverResp;
	    	}
	    }
	    if (ortn == errSSLWouldBlock) {
	        ortn = noErr;
	    }
	}
	
serverResp:
	if(pause) {
		doPause("Client GET msg received");
	}

	/* send out canned response */
	length = strlen(outMsg);
 	ortn = SSLWrite(ctx, outMsg, length, &length);
 	if(ortn) {
 		printSslErrStr("SSLWrite", ortn);
 	}
	if(pause) {
		doPause("Server response sent");
	}
    if (ortn == noErr) {
        ortn = SSLClose(ctx);
	}
cleanup:
	if(acceptSock) {
		endpointShutdown(acceptSock);
	}
	if(ctx) {
	    SSLDisposeContext(ctx);  
	}    
	/* FIXME - dispose of serverCerts */
	return ortn;
}
/*
 * params->lock is held for us by runSession() - we use it as a semapahore by
 * unlocking it when we've created a port to listen on. 
 * This is generally run from a thread via sslRunSession() and 
 * sslServerThread() in sslAppUtils.cpp. 
 */
OSStatus sslAppServe(
	SslAppTestParams	*params)
{
	otSocket			listenSock = 0;
	otSocket			acceptSock = 0;
    PeerSpec            peerId;
    OSStatus            ortn;
    SSLContextRef       ctx = NULL;
	SecKeychainRef		serverKc = nil;
	CFArrayRef			serverCerts = nil;
	
	sslThrDebug("Server", "starting");
    params->negVersion = kSSLProtocolUnknown;
    params->negCipher = SSL_NULL_WITH_NULL_NULL;
	params->ortn = noHardwareErr;
    
	/* set up a socket on which to listen */
	for(unsigned retry=0; retry<BIND_RETRIES; retry++) {
		ortn = ListenForClients(params->port, params->nonBlocking,
			&listenSock);
		switch(ortn) {
			case errSecSuccess:
				break;
			case errSecOpWr:
				/* port already in use - try another */
				params->port++;
				if(params->verbose || THREADING_DEBUG) {
					printf("...retrying ListenForClients at port %d\n",
						params->port);
				}
				break;
			default:
				break;
		}
		if(ortn != errSecOpWr) {
			break;
		}
	}
	
	/* let main thread know a socket is ready */
	if(pthread_mutex_lock(&params->pthreadMutex)) {
		printf("***Error acquiring server lock; aborting.\n");
		return -1;
	}
	params->serverReady = true;
	if(pthread_cond_broadcast(&params->pthreadCond)) {
		printf("***Error waking main thread; aborting.\n");
		return -1;
	}
	if(pthread_mutex_unlock(&params->pthreadMutex)) {
		printf("***Error acquiring server lock; aborting.\n");
		return -1;
	}

	if(ortn) {
		printf("ListenForClients returned %d; aborting\n", (int)ortn);
		return ortn;
	}

	/* wait for a connection */
	if(params->verbose) {
		printf("Waiting for client connection...");
		fflush(stdout);
	}
	ortn = AcceptClientConnection(listenSock, &acceptSock, &peerId);
    if(ortn) {
    	printf("AcceptClientConnection returned %d; aborting\n", (int)ortn);
    	return ortn;
    }

	/* 
	 * Set up a SecureTransport session.
	 */
	ortn = SSLNewContext(true, &ctx);
	if(ortn) {
		printSslErrStr("SSLNewContext", ortn);
		goto cleanup;
	} 
	ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite);
	if(ortn) {
		printSslErrStr("SSLSetIOFuncs", ortn);
		goto cleanup;
	} 
	ortn = SSLSetConnection(ctx, (SSLConnectionRef)acceptSock);
	if(ortn) {
		printSslErrStr("SSLSetConnection", ortn);
		goto cleanup;
	}
	
	if(params->anchorFile) {
		ortn = sslAddTrustedRoot(ctx, params->anchorFile, 
			params->replaceAnchors);
		if(ortn) {
			goto cleanup;
		}
	}
	if(params->myCertKcName != NULL) {
		/* if not, better be trying anonymous diff-hellman... :-) */
		serverCerts = getSslCerts(params->myCertKcName, false, false, NULL,
			&serverKc);
		if(serverCerts == nil) {
			exit(1);
		}
		if(params->password) {
			ortn = SecKeychainUnlock(serverKc, strlen(params->password), 
					(void *)params->password, true);
			if(ortn) {
				printf("SecKeychainUnlock returned %d\n", (int)ortn);
				/* oh well */
			}
		}
		if(params->idIsTrustedRoot) {
			/* assume this is a root we want to implicitly trust */
			ortn = addIdentityAsTrustedRoot(ctx, serverCerts);
			if(ortn) {
				goto cleanup;
			}
		}
		ortn = SSLSetCertificate(ctx, serverCerts);
		if(ortn) {
			printSslErrStr("SSLSetCertificate", ortn);
			goto cleanup;
		}
	}
	
	if(params->disableCertVerify) {
		ortn = SSLSetEnableCertVerify(ctx, false);
		if(ortn) {
			printSslErrStr("SSLSetEnableCertVerify", ortn);
			goto cleanup;
		}
	}
	ortn = sslSetProtocols(ctx, params->acceptedProts, params->tryVersion);
	if(ortn) {
		goto cleanup;
	}
	if(params->resumeEnable) {
		ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec));
		if(ortn) {
			printSslErrStr("SSLSetPeerID", ortn);
			goto cleanup;
		}
	}
	if(params->ciphers != NULL) {
		ortn = sslSetEnabledCiphers(ctx, params->ciphers);
		if(ortn) {
			goto cleanup;
		}
	}
	if(params->authenticate != kNeverAuthenticate) {
		ortn = SSLSetClientSideAuthenticate(ctx, params->authenticate);
		if(ortn) {
			printSslErrStr("SSLSetClientSideAuthenticate", ortn);
			goto cleanup;
		}
	}
	if(params->dhParams) {
		#if JAGUAR_BUILD
		printf("***Diffie-Hellman not supported in this config.\n");
		#else
		ortn = SSLSetDiffieHellmanParams(ctx, params->dhParams, 
			params->dhParamsLen);
		if(ortn) {
			printSslErrStr("SSLSetDiffieHellmanParams", ortn);
			goto cleanup;
		}
		#endif
	}

	/* Perform SSL/TLS handshake */
    do {   
		ortn = SSLHandshake(ctx);
	    if((ortn == errSSLWouldBlock) && !params->silent) {
	    	/* keep UI responsive */ 
	    	sslOutputDot();
	    }
    } while (ortn == errSSLWouldBlock);
	
	SSLGetClientCertificateState(ctx, &params->certState);
	SSLGetNegotiatedCipher(ctx, &params->negCipher);
	SSLGetNegotiatedProtocolVersion(ctx, &params->negVersion);
	
	if(params->verbose) {
		printf("\n");
	}
	if(ortn) {
		goto cleanup;
	}

	/* wait for one complete line */
	char readBuf[READBUF_LEN];
	size_t length;
	while(ortn == errSecSuccess) {
	    length = READBUF_LEN;
	    ortn = SSLRead(ctx, readBuf, length, &length);
	    if (ortn == errSSLWouldBlock) {
			/* keep trying */
	        ortn = errSecSuccess;
			continue;
	    }
	    if(length == 0) {
			/* keep trying */
			continue;
	    }
	    
	    /* poor person's line completion scan */
	    for(unsigned i=0; i<length; i++) {
	    	if((readBuf[i] == '\n') || (readBuf[i] == '\r')) {
	    		goto serverResp;
	    	}
	    }
	}
	
serverResp:
	/* send out canned response */
	ortn = SSLWrite(ctx, SERVER_MESSAGE, strlen(SERVER_MESSAGE), &length);
	if(ortn) {
		printSslErrStr("SSLWrite", ortn);
	}

cleanup:
	/*
	 * always do close, even on error - to flush outgoing write queue 
	 */
	if(ctx) {
		OSStatus cerr = SSLClose(ctx);
		if(ortn == errSecSuccess) {
			ortn = cerr;
		}
	}
	if(acceptSock) {
		while(!params->clientDone && !params->serverAbort) {
			usleep(100);
		}
		endpointShutdown(acceptSock);
	}
	if(listenSock) {
		endpointShutdown(listenSock);
	}
	if(ctx) {
	    SSLDisposeContext(ctx);  
	}    
	params->ortn = ortn;
	sslThrDebug("Server", "done");
	return ortn;
}
Exemple #8
0
int					/* O - Exit status */
main(int  argc,				/* I - Number of command-line arguments */
     char *argv[])			/* I - Command-line arguments */
{
  int		i;			/* Looping var */
  http_t	*http;			/* HTTP connection */
  const char	*server = NULL;		/* Hostname from command-line */
  int		port = 0;		/* Port number */
  const char	*cipherName = "UNKNOWN";/* Cipher suite name */
  int		dhBits = 0;		/* Diffie-Hellman bits */
  int		tlsVersion = 0;		/* TLS version number */
  char		uri[1024],		/* Printer URI */
		scheme[32],		/* URI scheme */
		host[256],		/* Hostname */
		userpass[256],		/* Username/password */
		resource[256];		/* Resource path */
  int		af = AF_UNSPEC,		/* Address family */
		tls_options = _HTTP_TLS_NONE,
					/* TLS options */
		tls_min_version = _HTTP_TLS_1_0,
		tls_max_version = _HTTP_TLS_MAX,
		verbose = 0;		/* Verbosity */
  ipp_t		*request,		/* IPP Get-Printer-Attributes request */
		*response;		/* IPP Get-Printer-Attributes response */
  ipp_attribute_t *attr;		/* Current attribute */
  const char	*name;			/* Attribute name */
  char		value[1024];		/* Attribute (string) value */
  static const char * const pattrs[] =	/* Requested attributes */
  {
    "color-supported",
    "compression-supported",
    "document-format-supported",
    "pages-per-minute",
    "printer-location",
    "printer-make-and-model",
    "printer-state",
    "printer-state-reasons",
    "sides-supported",
    "uri-authentication-supported",
    "uri-security-supported"
  };


  for (i = 1; i < argc; i ++)
  {
    if (!strcmp(argv[i], "--dh"))
    {
      tls_options |= _HTTP_TLS_ALLOW_DH;
    }
    else if (!strcmp(argv[i], "--no-cbc"))
    {
      tls_options |= _HTTP_TLS_DENY_CBC;
    }
    else if (!strcmp(argv[i], "--no-tls10"))
    {
      tls_min_version = _HTTP_TLS_1_1;
    }
    else if (!strcmp(argv[i], "--tls10"))
    {
      tls_min_version = _HTTP_TLS_1_0;
      tls_max_version = _HTTP_TLS_1_0;
    }
    else if (!strcmp(argv[i], "--rc4"))
    {
      tls_options |= _HTTP_TLS_ALLOW_RC4;
    }
    else if (!strcmp(argv[i], "--verbose") || !strcmp(argv[i], "-v"))
    {
      verbose = 1;
    }
    else if (!strcmp(argv[i], "-4"))
    {
      af = AF_INET;
    }
    else if (!strcmp(argv[i], "-6"))
    {
      af = AF_INET6;
    }
    else if (argv[i][0] == '-')
    {
      printf("tlscheck: Unknown option '%s'.\n", argv[i]);
      usage();
    }
    else if (!server)
    {
      if (!strncmp(argv[i], "ipps://", 7))
      {
        httpSeparateURI(HTTP_URI_CODING_ALL, argv[i], scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource));
        server = host;
      }
      else
      {
        server = argv[i];
        strlcpy(resource, "/ipp/print", sizeof(resource));
      }
    }
    else if (!port && (argv[i][0] == '=' || isdigit(argv[i][0] & 255)))
    {
      if (argv[i][0] == '=')
	port = atoi(argv[i] + 1);
      else
	port = atoi(argv[i]);
    }
    else
    {
      printf("tlscheck: Unexpected argument '%s'.\n", argv[i]);
      usage();
    }
  }

  if (!server)
    usage();

  if (!port)
    port = 631;

  _httpTLSSetOptions(tls_options, tls_min_version, tls_max_version);

  http = httpConnect2(server, port, NULL, af, HTTP_ENCRYPTION_ALWAYS, 1, 30000, NULL);
  if (!http)
  {
    printf("%s: ERROR (%s)\n", server, cupsLastErrorString());
    return (1);
  }

#ifdef __APPLE__
  SSLProtocol protocol;
  SSLCipherSuite cipher;
  char unknownCipherName[256];
  int paramsNeeded = 0;
  const void *params;
  size_t paramsLen;
  OSStatus err;

  if ((err = SSLGetNegotiatedProtocolVersion(http->tls, &protocol)) != noErr)
  {
    printf("%s: ERROR (No protocol version - %d)\n", server, (int)err);
    httpClose(http);
    return (1);
  }

  switch (protocol)
  {
    default :
        tlsVersion = 0;
        break;
    case kSSLProtocol3 :
        tlsVersion = 30;
        break;
    case kTLSProtocol1 :
        tlsVersion = 10;
        break;
    case kTLSProtocol11 :
        tlsVersion = 11;
        break;
    case kTLSProtocol12 :
        tlsVersion = 12;
        break;
  }

  if ((err = SSLGetNegotiatedCipher(http->tls, &cipher)) != noErr)
  {
    printf("%s: ERROR (No cipher suite - %d)\n", server, (int)err);
    httpClose(http);
    return (1);
  }

  switch (cipher)
  {
    case TLS_NULL_WITH_NULL_NULL:
	cipherName = "TLS_NULL_WITH_NULL_NULL";
	break;
    case TLS_RSA_WITH_NULL_MD5:
	cipherName = "TLS_RSA_WITH_NULL_MD5";
	break;
    case TLS_RSA_WITH_NULL_SHA:
	cipherName = "TLS_RSA_WITH_NULL_SHA";
	break;
    case TLS_RSA_WITH_RC4_128_MD5:
	cipherName = "TLS_RSA_WITH_RC4_128_MD5";
	break;
    case TLS_RSA_WITH_RC4_128_SHA:
	cipherName = "TLS_RSA_WITH_RC4_128_SHA";
	break;
    case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
	cipherName = "TLS_RSA_WITH_3DES_EDE_CBC_SHA";
	break;
    case TLS_RSA_WITH_NULL_SHA256:
	cipherName = "TLS_RSA_WITH_NULL_SHA256";
	break;
    case TLS_RSA_WITH_AES_128_CBC_SHA256:
	cipherName = "TLS_RSA_WITH_AES_128_CBC_SHA256";
	break;
    case TLS_RSA_WITH_AES_256_CBC_SHA256:
	cipherName = "TLS_RSA_WITH_AES_256_CBC_SHA256";
	break;
    case TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
	cipherName = "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
	cipherName = "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
	cipherName = "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
	cipherName = "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
	cipherName = "TLS_DH_DSS_WITH_AES_128_CBC_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
	cipherName = "TLS_DH_RSA_WITH_AES_128_CBC_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
	cipherName = "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
	cipherName = "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
	cipherName = "TLS_DH_DSS_WITH_AES_256_CBC_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
	cipherName = "TLS_DH_RSA_WITH_AES_256_CBC_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
	cipherName = "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
	cipherName = "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_DH_anon_WITH_RC4_128_MD5:
	cipherName = "TLS_DH_anon_WITH_RC4_128_MD5";
	paramsNeeded = 1;
	break;
    case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
	cipherName = "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
	cipherName = "TLS_DH_anon_WITH_AES_128_CBC_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_DH_anon_WITH_AES_256_CBC_SHA256:
	cipherName = "TLS_DH_anon_WITH_AES_256_CBC_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_PSK_WITH_RC4_128_SHA:
	cipherName = "TLS_PSK_WITH_RC4_128_SHA";
	break;
    case TLS_PSK_WITH_3DES_EDE_CBC_SHA:
	cipherName = "TLS_PSK_WITH_3DES_EDE_CBC_SHA";
	break;
    case TLS_PSK_WITH_AES_128_CBC_SHA:
	cipherName = "TLS_PSK_WITH_AES_128_CBC_SHA";
	break;
    case TLS_PSK_WITH_AES_256_CBC_SHA:
	cipherName = "TLS_PSK_WITH_AES_256_CBC_SHA";
	break;
    case TLS_DHE_PSK_WITH_RC4_128_SHA:
	cipherName = "TLS_DHE_PSK_WITH_RC4_128_SHA";
	paramsNeeded = 1;
	break;
    case TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
	cipherName = "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
	cipherName = "TLS_DHE_PSK_WITH_AES_128_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
	cipherName = "TLS_DHE_PSK_WITH_AES_256_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_RSA_PSK_WITH_RC4_128_SHA:
	cipherName = "TLS_RSA_PSK_WITH_RC4_128_SHA";
	break;
    case TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
	cipherName = "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA";
	break;
    case TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
	cipherName = "TLS_RSA_PSK_WITH_AES_128_CBC_SHA";
	break;
    case TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
	cipherName = "TLS_RSA_PSK_WITH_AES_256_CBC_SHA";
	break;
    case TLS_PSK_WITH_NULL_SHA:
	cipherName = "TLS_PSK_WITH_NULL_SHA";
	break;
    case TLS_DHE_PSK_WITH_NULL_SHA:
	cipherName = "TLS_DHE_PSK_WITH_NULL_SHA";
	paramsNeeded = 1;
	break;
    case TLS_RSA_PSK_WITH_NULL_SHA:
	cipherName = "TLS_RSA_PSK_WITH_NULL_SHA";
	break;
    case TLS_RSA_WITH_AES_128_GCM_SHA256:
	cipherName = "TLS_RSA_WITH_AES_128_GCM_SHA256";
	break;
    case TLS_RSA_WITH_AES_256_GCM_SHA384:
	cipherName = "TLS_RSA_WITH_AES_256_GCM_SHA384";
	break;
    case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
	cipherName = "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
	cipherName = "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384";
	paramsNeeded = 1;
	break;
    case TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
	cipherName = "TLS_DH_RSA_WITH_AES_128_GCM_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
	cipherName = "TLS_DH_RSA_WITH_AES_256_GCM_SHA384";
	paramsNeeded = 1;
	break;
    case TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
	cipherName = "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
	cipherName = "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384";
	paramsNeeded = 1;
	break;
    case TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
	cipherName = "TLS_DH_DSS_WITH_AES_128_GCM_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
	cipherName = "TLS_DH_DSS_WITH_AES_256_GCM_SHA384";
	paramsNeeded = 1;
	break;
    case TLS_DH_anon_WITH_AES_128_GCM_SHA256:
	cipherName = "TLS_DH_anon_WITH_AES_128_GCM_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_DH_anon_WITH_AES_256_GCM_SHA384:
	cipherName = "TLS_DH_anon_WITH_AES_256_GCM_SHA384";
	paramsNeeded = 1;
	break;
    case TLS_PSK_WITH_AES_128_GCM_SHA256:
	cipherName = "TLS_PSK_WITH_AES_128_GCM_SHA256";
	break;
    case TLS_PSK_WITH_AES_256_GCM_SHA384:
	cipherName = "TLS_PSK_WITH_AES_256_GCM_SHA384";
	break;
    case TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
	cipherName = "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
	cipherName = "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384";
	paramsNeeded = 1;
	break;
    case TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
	cipherName = "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256";
	break;
    case TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
	cipherName = "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384";
	break;
    case TLS_PSK_WITH_AES_128_CBC_SHA256:
	cipherName = "TLS_PSK_WITH_AES_128_CBC_SHA256";
	break;
    case TLS_PSK_WITH_AES_256_CBC_SHA384:
	cipherName = "TLS_PSK_WITH_AES_256_CBC_SHA384";
	break;
    case TLS_PSK_WITH_NULL_SHA256:
	cipherName = "TLS_PSK_WITH_NULL_SHA256";
	break;
    case TLS_PSK_WITH_NULL_SHA384:
	cipherName = "TLS_PSK_WITH_NULL_SHA384";
	break;
    case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
	cipherName = "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
	cipherName = "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384";
	paramsNeeded = 1;
	break;
    case TLS_DHE_PSK_WITH_NULL_SHA256:
	cipherName = "TLS_DHE_PSK_WITH_NULL_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_DHE_PSK_WITH_NULL_SHA384:
	cipherName = "TLS_DHE_PSK_WITH_NULL_SHA384";
	paramsNeeded = 1;
	break;
    case TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
	cipherName = "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256";
	break;
    case TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
	cipherName = "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384";
	break;
    case TLS_RSA_PSK_WITH_NULL_SHA256:
	cipherName = "TLS_RSA_PSK_WITH_NULL_SHA256";
	break;
    case TLS_RSA_PSK_WITH_NULL_SHA384:
	cipherName = "TLS_RSA_PSK_WITH_NULL_SHA384";
	break;
    case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
	cipherName = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
	cipherName = "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
	cipherName = "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
	cipherName = "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384";
	paramsNeeded = 1;
	break;
    case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
	cipherName = "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
	cipherName = "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
	cipherName = "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
	cipherName = "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384";
	paramsNeeded = 1;
	break;
    case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
	cipherName = "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
	cipherName = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
	cipherName = "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
	cipherName = "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384";
	paramsNeeded = 1;
	break;
    case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
	cipherName = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
	cipherName = "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
	cipherName = "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
	cipherName = "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384";
	paramsNeeded = 1;
	break;
    case TLS_RSA_WITH_AES_128_CBC_SHA:
	cipherName = "TLS_RSA_WITH_AES_128_CBC_SHA";
	break;
    case TLS_DH_DSS_WITH_AES_128_CBC_SHA:
	cipherName = "TLS_DH_DSS_WITH_AES_128_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_DH_RSA_WITH_AES_128_CBC_SHA:
	cipherName = "TLS_DH_RSA_WITH_AES_128_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
	cipherName = "TLS_DHE_DSS_WITH_AES_128_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
	cipherName = "TLS_DHE_RSA_WITH_AES_128_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_DH_anon_WITH_AES_128_CBC_SHA:
	cipherName = "TLS_DH_anon_WITH_AES_128_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_RSA_WITH_AES_256_CBC_SHA:
	cipherName = "TLS_RSA_WITH_AES_256_CBC_SHA";
	break;
    case TLS_DH_DSS_WITH_AES_256_CBC_SHA:
	cipherName = "TLS_DH_DSS_WITH_AES_256_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_DH_RSA_WITH_AES_256_CBC_SHA:
	cipherName = "TLS_DH_RSA_WITH_AES_256_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
	cipherName = "TLS_DHE_DSS_WITH_AES_256_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
	cipherName = "TLS_DHE_RSA_WITH_AES_256_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_DH_anon_WITH_AES_256_CBC_SHA:
	cipherName = "TLS_DH_anon_WITH_AES_256_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_ECDSA_WITH_NULL_SHA:
	cipherName = "TLS_ECDH_ECDSA_WITH_NULL_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
	cipherName = "TLS_ECDH_ECDSA_WITH_RC4_128_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
	cipherName = "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
	cipherName = "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
	cipherName = "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDHE_ECDSA_WITH_NULL_SHA:
	cipherName = "TLS_ECDHE_ECDSA_WITH_NULL_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
	cipherName = "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
	cipherName = "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
	cipherName = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
	cipherName = "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_RSA_WITH_NULL_SHA:
	cipherName = "TLS_ECDH_RSA_WITH_NULL_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_RSA_WITH_RC4_128_SHA:
	cipherName = "TLS_ECDH_RSA_WITH_RC4_128_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
	cipherName = "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
	cipherName = "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
	cipherName = "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDHE_RSA_WITH_NULL_SHA:
	cipherName = "TLS_ECDHE_RSA_WITH_NULL_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
	cipherName = "TLS_ECDHE_RSA_WITH_RC4_128_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
	cipherName = "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
	cipherName = "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
	cipherName = "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_anon_WITH_NULL_SHA:
	cipherName = "TLS_ECDH_anon_WITH_NULL_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_anon_WITH_RC4_128_SHA:
	cipherName = "TLS_ECDH_anon_WITH_RC4_128_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA:
	cipherName = "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_anon_WITH_AES_128_CBC_SHA:
	cipherName = "TLS_ECDH_anon_WITH_AES_128_CBC_SHA";
	paramsNeeded = 1;
	break;
    case TLS_ECDH_anon_WITH_AES_256_CBC_SHA:
	cipherName = "TLS_ECDH_anon_WITH_AES_256_CBC_SHA";
	paramsNeeded = 1;
	break;
    default :
        snprintf(unknownCipherName, sizeof(unknownCipherName), "UNKNOWN_%04X", cipher);
        cipherName = unknownCipherName;
        break;
  }

  if (cipher == TLS_RSA_WITH_RC4_128_MD5 ||
      cipher == TLS_RSA_WITH_RC4_128_SHA)
  {
    printf("%s: ERROR (Printers MUST NOT negotiate RC4 cipher suites.)\n", server);
    httpClose(http);
    return (1);
  }

  if ((err = SSLGetDiffieHellmanParams(http->tls, &params, &paramsLen)) != noErr && paramsNeeded)
  {
    printf("%s: ERROR (Unable to get Diffie-Hellman parameters - %d)\n", server, (int)err);
    httpClose(http);
    return (1);
  }

  if (paramsLen < 128 && paramsLen != 0)
  {
    printf("%s: ERROR (Diffie-Hellman parameters MUST be at least 2048 bits, but Printer uses only %d bits/%d bytes)\n", server, (int)paramsLen * 8, (int)paramsLen);
    httpClose(http);
    return (1);
  }

  dhBits = (int)paramsLen * 8;
#endif /* __APPLE__ */

  if (dhBits > 0)
    printf("%s: OK (TLS: %d.%d, %s, %d DH bits)\n", server, tlsVersion / 10, tlsVersion % 10, cipherName, dhBits);
  else
    printf("%s: OK (TLS: %d.%d, %s)\n", server, tlsVersion / 10, tlsVersion % 10, cipherName);

  if (verbose)
  {
    httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipps", NULL, host, port, resource);
    request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);

    response = cupsDoRequest(http, request, resource);

    for (attr = ippFirstAttribute(response); attr; attr = ippNextAttribute(response))
    {
      if (ippGetGroupTag(attr) != IPP_TAG_PRINTER)
        continue;

      if ((name = ippGetName(attr)) == NULL)
        continue;

      ippAttributeString(attr, value, sizeof(value));
      printf("    %s=%s\n", name, value);
    }

    ippDelete(response);
  }

  httpClose(http);

  return (0);
}
int dtls_client(const char *hostname, int bypass)
{
    int fd;
    int tlsfd;
    struct sockaddr_in sa;
    
    printf("Running dtls_client test with hostname=%s, bypass=%d\n", hostname, bypass);

    if ((fd=socket(AF_INET, SOCK_DGRAM, 0))==-1) {
        perror("socket");
        exit(-1);
    }
    
    memset((char *) &sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_port = htons(PORT);
    if (inet_aton(hostname, &sa.sin_addr)==0) {
        fprintf(stderr, "inet_aton() failed\n");
        exit(1);
    }
    
    if(connect(fd, (struct sockaddr *)&sa, sizeof(sa))==-1)
    {
        perror("connect");
        return errno;
    }
    
    /* Change to non blocking io */
    fcntl(fd, F_SETFL, O_NONBLOCK);
    
    SSLRecordContextRef c=(intptr_t)fd;
    
    
    OSStatus            ortn;
    SSLContextRef       ctx = NULL;
    
    SSLClientCertificateState certState;
    SSLCipherSuite negCipher;
    SSLProtocol negVersion;
    
	/*
	 * Set up a SecureTransport session.
	 */
    
    ctx = SSLCreateContextWithRecordFuncs(kCFAllocatorDefault, kSSLClientSide, kSSLDatagramType, &TLSSocket_Funcs);
    if(!ctx) {
        printSslErrStr("SSLCreateContextWithRecordFuncs", -1);
        return -1;
    }

    printf("Attaching filter\n");
    ortn = TLSSocket_Attach(fd);
    if(ortn) {
		printSslErrStr("TLSSocket_Attach", ortn);
		return ortn;        
    }
    
    if(bypass) {
        tlsfd = open("/dev/tlsnke", O_RDWR);
        if(tlsfd<0) {
            perror("opening tlsnke dev");
            exit(-1);
        }
    }

    ortn = SSLSetRecordContext(ctx, c);
	if(ortn) {
		printSslErrStr("SSLSetRecordContext", ortn);
		return ortn;
	}
    
    ortn = SSLSetMaxDatagramRecordSize(ctx, 600);
    if(ortn) {
		printSslErrStr("SSLSetMaxDatagramRecordSize", ortn);
        return ortn;
	}
    
    /* Lets not verify the cert, which is a random test cert */
    ortn = SSLSetEnableCertVerify(ctx, false);
    if(ortn) {
        printSslErrStr("SSLSetEnableCertVerify", ortn);
        return ortn;
    }
    
    ortn = SSLSetCertificate(ctx, server_chain());
    if(ortn) {
        printSslErrStr("SSLSetCertificate", ortn);
        return ortn;
    }
    
    printf("Handshake...\n");

    do {
		ortn = SSLHandshake(ctx);
	    if(ortn == errSSLWouldBlock) {
            /* keep UI responsive */
            sslOutputDot();
	    }
    } while (ortn == errSSLWouldBlock);
    
    
    SSLGetClientCertificateState(ctx, &certState);
	SSLGetNegotiatedCipher(ctx, &negCipher);
	SSLGetNegotiatedProtocolVersion(ctx, &negVersion);
    
    int count;
    size_t len;
    ssize_t sreadLen, swriteLen;
    size_t readLen, writeLen;

    char buffer[BUFLEN];
    
    count = 0;
    while(count<COUNT) {
        int timeout = 10000;
        
        snprintf(buffer, BUFLEN, "Message %d", count);
        len = strlen(buffer);
        
        if(bypass) {
            /* Send data through the side channel, kind of like utun would */
            swriteLen=write(tlsfd, buffer, len);
            if(swriteLen<0) {
                perror("write to tlsfd");
                break;
            }
            writeLen=swriteLen;
        } else {
            ortn=SSLWrite(ctx, buffer, len, &writeLen);
            if(ortn) {
                printSslErrStr("SSLWrite", ortn);
                break;
            }
        }

        printf("Wrote %lu bytes\n", writeLen);
        
        count++;
        
        if(bypass) {
            do {
                sreadLen=read(tlsfd, buffer, BUFLEN);
            } while((sreadLen==-1) && (errno==EAGAIN) && (timeout--));
            if((sreadLen==-1) && (errno==EAGAIN)) {
                printf("Read timeout...\n");
                continue;
            }
            if(sreadLen<0) {
                perror("read from tlsfd");
                break;
            }
            readLen=sreadLen;
        }
        else {
            do {
                ortn=SSLRead(ctx, buffer, BUFLEN, &readLen);
            } while((ortn==errSSLWouldBlock) && (timeout--));
            if(ortn==errSSLWouldBlock) {
                printf("SSLRead timeout...\n");
                continue;
            }
            if(ortn) {
                printSslErrStr("SSLRead", ortn);
                break;
            }
        }

        buffer[readLen]=0;
        printf("Received %lu bytes: %s\n", readLen, buffer);
        
    }
    
    SSLClose(ctx);
    
    SSLDisposeContext(ctx);
    
    return ortn;
}
int main(int argc, char **argv)
{
	/* user-spec'd variables */
	const char 		*kcName = DEFAULT_KC;
	unsigned 		xferSize = XFERSIZE_DEF;
	int 			port = PORT_DEF;
	const char 		*hostName = HOST_DEF;
	SSLCipherSuite 	cipherSuite = TLS_RSA_WITH_AES_128_CBC_SHA;
	SSLProtocol 	prot = kTLSProtocol1Only;
	char 			password[200];
	bool 			clientAuthEnable = false;
	bool 			isServer = false;
	unsigned 		bufSize = BUFSIZE;
	bool			diffieHellman = false;
	int				nonBlocking = 0;
	
	if(argc < 2) {
		usage(argv);
	}
	password[0] = 0;
	switch(argv[1][0]) {
		case 's':
			isServer = true;
			break;
		case 'c':
			isServer = false;
			break;
		default:
			usage(argv);
	}
	
	extern int optind;
	extern char *optarg;
	int arg;
	optind = 2;
	while ((arg = getopt(argc, argv, "h:p:k:x:c:v:w:b:aB")) != -1) {
		switch (arg) {
			case 'h':
				hostName = optarg;
				break;
			case 'p':
				port = atoi(optarg);
				break;
			case 'k':
				kcName = optarg;
				break;
			case 'x':
				xferSize = atoi(optarg);
				break;
			case 'c':
				if(!isServer) {
					printf("***Specify cipherSuite on server side.\n");
					exit(1);
				}
				switch(optarg[0]) {
					case 'r':
						cipherSuite = SSL_RSA_WITH_RC4_128_SHA;
						break;
					case 'd':
						cipherSuite = SSL_RSA_WITH_DES_CBC_SHA;
						break;
					case 'D':
						cipherSuite = SSL_RSA_WITH_3DES_EDE_CBC_SHA;
						break;
					case 'h':
						cipherSuite = SSL_DH_anon_WITH_RC4_128_MD5;
						diffieHellman = true;
						break;
					case 'H':
						cipherSuite = SSL_DHE_DSS_WITH_DES_CBC_SHA;
						diffieHellman = true;
						break;
					case 'A':
						cipherSuite = TLS_RSA_WITH_AES_256_CBC_SHA;
						break;
					default:
						usage(argv);
				}
				break;
			case 'v':
				if(!isServer) {
					printf("***Specify protocol on server side.\n");
					exit(1);
				}
				switch(optarg[0]) {
					case 't':
						prot = kTLSProtocol1Only;
						break;
					case '2':
						prot = kSSLProtocol2;
						break;
					case '3':
						prot = kSSLProtocol3Only;
						break;
					default:
						usage(argv);
				}
				break;
			case 'w':
				strcpy(password, optarg);
				break;
			case 'b':
				bufSize = atoi(optarg);
				break;
			case 'a':
				clientAuthEnable = true;
				break;
			case 'B':
				nonBlocking = 1;
				break;
			default:
				usage(argv);
		}
	}
	
	/* per-transfer buffer - make it random for server */
	char *buf = (char *)malloc(bufSize);
	if(isServer) {
		Security::DevRandomGenerator rng;
		rng.random(buf, bufSize);
	}
	
	/* gather Diffie-Hellman params from cwd */
	unsigned char *dhParams = NULL;
	unsigned dhParamsLen = 0;
	if(diffieHellman && isServer) {
		if(readFile(DH_PARAM_FILE, &dhParams, &dhParamsLen)) {
			printf("***Error reading Diffie-Hellman Params. Prepare to "
				"wait for a minute during SSL handshake.\n");
		}
	}
	
	/*
	 * Open keychain; both sides use the same one.
	 */
	OSStatus ortn;
	SecKeychainRef certKc = NULL;
	CFAbsoluteTime kcOpenStart = CFAbsoluteTimeGetCurrent();
	ortn = SecKeychainOpen(kcName, &certKc);
	if(ortn) {
		printf("Error opening keychain %s (%d); aborting.\n",
			kcName, (int)ortn);
		exit(1);
	}
	if(password[0]) {
		ortn = SecKeychainUnlock(certKc, strlen(password), password, true);
		if(ortn) {
			printf("SecKeychainUnlock returned %d\n", (int)ortn);
			/* oh well */
		}
	}
	CFAbsoluteTime kcOpenEnd = CFAbsoluteTimeGetCurrent();
	
	otSocket peerSock = 0;
	otSocket listenSock = 0;			// for server only
    PeerSpec peerId;
	
	if(isServer) {
		printf("...listening for client connection on port %d\n", port);
		ortn = ListenForClients(port, nonBlocking, &listenSock);
		if(ortn) {
			printf("...error establishing a listen socket. Aborting.\n");
			exit(1);
		}
		ortn = AcceptClientConnection(listenSock, &peerSock, &peerId);
		if(ortn) {
			printf("...error listening for connection. Aborting.\n");
			exit(1);
		}
	}
	else {
		printf("...connecting to host %s at port %d\n", hostName, port);
		ortn = MakeServerConnection(hostName, port, nonBlocking, &peerSock,
			&peerId);
		if(ortn) {
			printf("...error connecting to server %s. Aborting.\n",
				hostName);
			exit(1);
		}
	}

	/* start timing SSL setup */
	CFAbsoluteTime setupStart = CFAbsoluteTimeGetCurrent();

	SSLContextRef ctx;
	ortn = SSLNewContext(isServer, &ctx);
	if(ortn) {
		printSslErrStr("SSLNewContext", ortn);
		exit(1);
	} 
	ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite);
	if(ortn) {
		printSslErrStr("SSLSetIOFuncs", ortn);
		exit(1);
	} 
	ortn = SSLSetConnection(ctx, (SSLConnectionRef)peerSock);
	if(ortn) {
		printSslErrStr("SSLSetConnection", ortn);
		exit(1);
	}
	ortn = SSLSetPeerDomainName(ctx, hostName, strlen(hostName) + 1);
	if(ortn) {
		printSslErrStr("SSLSetPeerDomainName", ortn);
		exit(1);
	}	
	
	/*
	 * Server/client specific setup.
	 *
	 * Client uses the same keychain as server, but it uses it for 
	 * sslAddTrustedRoots() instead of getSslCerts() and 
	 * SSLSetCertificate().
	 */
	CFArrayRef myCerts = NULL;
	if(clientAuthEnable || isServer) {
		myCerts = sslKcRefToCertArray(certKc, CSSM_FALSE, CSSM_FALSE, NULL, NULL);
		if(myCerts == NULL) {
			exit(1);
		}
		ortn = addIdentityAsTrustedRoot(ctx, myCerts);
		if(ortn) {
			exit(1);
		}
		ortn = SSLSetCertificate(ctx, myCerts);
		if(ortn) {
			printSslErrStr("SSLSetCertificate", ortn);
			exit(1);
		}
	}
	if(isServer) {
		SSLAuthenticate auth;
		if(clientAuthEnable) {
			auth = kAlwaysAuthenticate;
		}
		else {
			auth = kNeverAuthenticate;
		}
		ortn = SSLSetClientSideAuthenticate(ctx, auth);
		if(ortn) {
			printSslErrStr("SSLSetClientSideAuthenticate", ortn);
			exit(1);
		}
		ortn = SSLSetEnabledCiphers(ctx, &cipherSuite, 1);
		if(ortn) {
			printSslErrStr("SSLSetEnabledCiphers", ortn);
			exit(1);
		}
		ortn = SSLSetProtocolVersion(ctx, prot);
		if(ortn) {
			printSslErrStr("SSLSetProtocolVersion", ortn);
			exit(1);
		}
		if(dhParams != NULL) {
			ortn = SSLSetDiffieHellmanParams(ctx, dhParams, dhParamsLen);
			if(ortn) {
				printSslErrStr("SSLSetDiffieHellmanParams", ortn);
				exit(1);
			}
		}
	}
	else {
		/* client setup */
		if(!clientAuthEnable) {
			/* We're not presenting a cert; trust the server certs */
			bool foundOne;
			ortn = sslAddTrustedRoots(ctx, certKc, &foundOne);
			if(ortn) {
				printSslErrStr("sslAddTrustedRoots", ortn);
				exit(1);
			}
		}
	}
	
	/*
	 * Context setup complete. Start timing handshake.
	 */
	CFAbsoluteTime hshakeStart = CFAbsoluteTimeGetCurrent();
    do {   
		ortn = SSLHandshake(ctx);
    } while (ortn == errSSLWouldBlock);
	if(ortn) {
		printSslErrStr("SSLHandshake", ortn);
		exit(1);
	}
	CFAbsoluteTime hshakeEnd = CFAbsoluteTimeGetCurrent();
	
	/* snag these before data xfer possibly shuts down connection */
	SSLProtocol	negVersion;
	SSLCipherSuite negCipher;
	SSLClientCertificateState certState;		// RETURNED

	SSLGetNegotiatedCipher(ctx, &negCipher);
	SSLGetNegotiatedProtocolVersion(ctx, &negVersion);
	SSLGetClientCertificateState(ctx, &certState);
	
	/* server sends xferSize bytes to client and shuts down */
	size_t bytesMoved;
	
	CFAbsoluteTime dataStart = CFAbsoluteTimeGetCurrent();
	size_t totalMoved = 0;
	if(isServer) {
		size_t bytesToGo = xferSize;
		bool done = false;
		do {
			size_t thisMove = bufSize;
			if(thisMove > bytesToGo) {
				thisMove = bytesToGo;
			}
			ortn = SSLWrite(ctx, buf, thisMove, &bytesMoved);
			switch(ortn) {
				case noErr:
				case errSSLWouldBlock:
					break;
				default:
					done = true;
					break;
			}
			bytesToGo -= bytesMoved;
			totalMoved += bytesMoved;
			if(bytesToGo == 0) {
				done = true;
			}
		} while(!done);
		if(ortn != noErr) {
			printSslErrStr("SSLWrite", ortn);
			exit(1);
		}
	}
	else {
		/* client reads until error or errSSLClosedGraceful */
		bool done = false;
		do {
			ortn = SSLRead(ctx, buf, bufSize, &bytesMoved);
			switch(ortn) {
				case errSSLClosedGraceful:
					done = true;
					break;
				case noErr:
				case errSSLWouldBlock:
					break;
				default:
					done = true;
					break;
			}
			totalMoved += bytesMoved;
		} while(!done);
		if(ortn != errSSLClosedGraceful) {
			printSslErrStr("SSLRead", ortn);
			exit(1);
		}
	}
	
	/* shut down channel */
	ortn = SSLClose(ctx);
	if(ortn) {
		printSslErrStr("SSLCLose", ortn);
		exit(1);
	}
	CFAbsoluteTime dataEnd = CFAbsoluteTimeGetCurrent();

	/* how'd we do? */
	printf("SSL version          : %s\n", 
		sslGetProtocolVersionString(negVersion));
	printf("CipherSuite          : %s\n",
		sslGetCipherSuiteString(negCipher));
	printf("Client Cert State    : %s\n",
			sslGetClientCertStateString(certState));

	if(password[0]) {
		printf("keychain open/unlock : ");
	}
	else {
		printf("keychain open        : ");
	}
	printf("%f s\n", kcOpenEnd - kcOpenStart);
	printf("SSLContext setup     : %f s\n", hshakeStart - setupStart);
	printf("SSL Handshake        : %f s\n", hshakeEnd - hshakeStart);
	printf("Data Transfer        : %u bytes in %f s\n", (unsigned)totalMoved, 
		dataEnd - dataStart);
	printf("                     : %.1f Kbytes/s\n", 
			totalMoved / (dataEnd - dataStart) / 1024.0);
	return 0;
}
/* relies on SSLSetProtocolVersionEnabled */
OSStatus sslAppClient(
	SslAppTestParams	*params)
{
    PeerSpec            peerId;
	otSocket			sock = 0;
    OSStatus            ortn;
    SSLContextRef       ctx = NULL;
	SecKeychainRef		clientKc = nil;
	CFArrayRef			clientCerts = nil;
	
	sslThrDebug("Client", "starting");
    params->negVersion = kSSLProtocolUnknown;
    params->negCipher  = SSL_NULL_WITH_NULL_NULL;
    params->ortn       = noHardwareErr;
	
	/* first make sure requested server is there */
	ortn = MakeServerConnection(params->hostName, params->port,
		params->nonBlocking, &sock, &peerId);
    if(ortn) {
    	printf("MakeServerConnection returned %d; aborting\n", (int)ortn);
    	return ortn;
    }
	
	/* 
	 * Set up a SecureTransport session.
	 */
	ortn = SSLNewContext(false, &ctx);
	if(ortn) {
		printSslErrStr("SSLNewContext", ortn);
		goto cleanup;
	} 
	ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite);
	if(ortn) {
		printSslErrStr("SSLSetIOFuncs", ortn);
		goto cleanup;
	} 
	ortn = SSLSetConnection(ctx, (SSLConnectionRef)sock);
	if(ortn) {
		printSslErrStr("SSLSetConnection", ortn);
		goto cleanup;
	}
	if(!params->skipHostNameCheck) {
		ortn = SSLSetPeerDomainName(ctx, params->hostName, 
			strlen(params->hostName));
		if(ortn) {
			printSslErrStr("SSLSetPeerDomainName", ortn);
			goto cleanup;
		}
	}
	
	/* remainder of setup is optional */
	if(params->anchorFile) {
		ortn = sslAddTrustedRoot(ctx, params->anchorFile, params->replaceAnchors);
		if(ortn) {
			goto cleanup;
		}
	}
	ortn = sslSetProtocols(ctx, params->acceptedProts, params->tryVersion);
	if(ortn) {
		goto cleanup;
	}
	if(params->resumeEnable) {
		ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec));
		if(ortn) {
			printSslErrStr("SSLSetPeerID", ortn);
			goto cleanup;
		}
	}
	if(params->disableCertVerify) {
		ortn = SSLSetEnableCertVerify(ctx, false);
		if(ortn) {
			printSslErrStr("SSLSetEnableCertVerify", ortn);
			goto cleanup;
		}
	}
	if(params->ciphers != NULL) {
		ortn = sslSetEnabledCiphers(ctx, params->ciphers);
		if(ortn) {
			goto cleanup;
		}
	}
	if(params->myCertKcName) {
		clientCerts = getSslCerts(params->myCertKcName, false, false, NULL, &clientKc);
		if(clientCerts == nil) {
			exit(1);
		}
		if(params->password) {
			ortn = SecKeychainUnlock(clientKc, strlen(params->password), 
					(void *)params->password, true);
			if(ortn) {
				printf("SecKeychainUnlock returned %d\n", (int)ortn);
				/* oh well */
			}
		}
		if(params->idIsTrustedRoot) {
			/* assume this is a root we want to implicitly trust */
			ortn = addIdentityAsTrustedRoot(ctx, clientCerts);
			if(ortn) {
				goto cleanup;
			}
		}
		ortn = SSLSetCertificate(ctx, clientCerts);
		if(ortn) {
			printSslErrStr("SSLSetCertificate", ortn);
			goto cleanup;
		}
	}
    do {
		ortn = SSLHandshake(ctx);
	    if((ortn == errSSLWouldBlock) && !params->silent) {
	    	/* keep UI responsive */ 
	    	sslOutputDot();
	    }
    } while (ortn == errSSLWouldBlock);
	
	SSLGetClientCertificateState(ctx, &params->certState);
	SSLGetNegotiatedCipher(ctx, &params->negCipher);
	SSLGetNegotiatedProtocolVersion(ctx, &params->negVersion);
	
	if(ortn != errSecSuccess) {
		goto cleanup;
	}

	/* send a GET msg */
	size_t actLen;
	ortn = SSLWrite(ctx, CLIENT_GETMSG, strlen(CLIENT_GETMSG), &actLen);
	if(ortn) {
		printSslErrStr("SSLWrite", ortn);
		goto cleanup;
	}
	
	#if KEEP_CONNECTED
	
	/*
	 * Consume any server data and wait for server to disconnect
	 */
	char readBuf[READBUF_LEN];
    do {
		ortn = SSLRead(ctx, readBuf, READBUF_LEN, &actLen);
    } while (ortn == errSSLWouldBlock);
	
    /* convert normal "shutdown" into zero err rtn */
	if(ortn == errSSLClosedGraceful) {
		ortn = errSecSuccess;
	}
	#endif	/* KEEP_CONNECTED */
	
cleanup:
	if(ctx) {
		OSStatus cerr = SSLClose(ctx);
		if(ortn == errSecSuccess) {
			ortn = cerr;
		}
	}
	if(sock) {
		endpointShutdown(sock);
	}
	if(ctx) {
	    SSLDisposeContext(ctx);  
	}    
	params->ortn = ortn;
	sslThrDebug("Client", "done");
	return ortn;
}