Exemple #1
0
/*
 * Given an SSLContextRef and an array of SSLCipherSuites, terminated by
 * SSL_NO_SUCH_CIPHERSUITE, select those SSLCipherSuites which the library
 * supports and do a SSLSetEnabledCiphers() specifying those. 
 */
static OSStatus setEnabledCiphers(
	SSLContextRef ctx,
	const SSLCipherSuite *ciphers)
{
	UInt32 numSupported;
	OSStatus ortn;
	SSLCipherSuite *supported = NULL;
	SSLCipherSuite *enabled = NULL;
	unsigned enabledDex = 0;	// index into enabled
	unsigned supportedDex = 0;	// index into supported
	unsigned inDex = 0;			// index into ciphers
	
	/* first get all the supported ciphers */
	ortn = SSLGetNumberSupportedCiphers(ctx, &numSupported);
	if(ortn) {
		printSslErrStr("SSLGetNumberSupportedCiphers", ortn);
		return ortn;
	}
	supported = (SSLCipherSuite *)malloc(numSupported * sizeof(SSLCipherSuite));
	ortn = SSLGetSupportedCiphers(ctx, supported, &numSupported);
	if(ortn) {
		printSslErrStr("SSLGetSupportedCiphers", ortn);
		return ortn;
	}
	
	/* 
	 * Malloc an array we'll use for SSLGetEnabledCiphers - this will  be
	 * bigger than the number of suites we actually specify 
	 */
	enabled = (SSLCipherSuite *)malloc(numSupported * sizeof(SSLCipherSuite));
	
	/* 
	 * For each valid suite in ciphers, see if it's in the list of 
	 * supported ciphers. If it is, add it to the list of ciphers to be
	 * enabled. 
	 */
	for(inDex=0; ciphers[inDex] != SSL_NO_SUCH_CIPHERSUITE; inDex++) {
		for(supportedDex=0; supportedDex<numSupported; supportedDex++) {
			if(ciphers[inDex] == supported[supportedDex]) {
				enabled[enabledDex++] = ciphers[inDex];
				break;
			}
		}
	}
	
	/* send it on down. */
	ortn = SSLSetEnabledCiphers(ctx, enabled, enabledDex);
	if(ortn) {
		printSslErrStr("SSLSetEnabledCiphers", ortn);
	}
	free(enabled);
	free(supported);
	return ortn;
}
static int test_SetEnabledCiphers(SSLContextRef ssl)
{
    int fail=1;
    size_t num_enabled;
    
    /* This should not fail as long as we have one valid cipher in this table */
    SSLCipherSuite ciphers[] = {
        SSL_RSA_WITH_RC2_CBC_MD5, /* unsupported */
        TLS_RSA_WITH_NULL_SHA, /* supported by not enabled by default */
        TLS_RSA_WITH_AES_128_CBC_SHA, /* Supported and enabled by default */
    };

    require_noerr(SSLSetEnabledCiphers(ssl, ciphers, sizeof(ciphers)/sizeof(SSLCipherSuite)), out);
    require_noerr(SSLGetNumberEnabledCiphers(ssl, &num_enabled), out);

    require(num_enabled==2, out); /* 2 ciphers in the above table are supported */

    /* Success! */
    fail=0;

out:
    return fail;
}
Exemple #3
0
BOOL schan_imp_create_session(schan_imp_session *session, schan_credentials *cred)
{
    struct mac_session *s;
    unsigned i;
    int status;

    TRACE("(%p)\n", session);

    s = heap_alloc(sizeof(*s));
    if (!s)
        return FALSE;

    InitializeCriticalSection(&s->cs);
    s->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": mac_session.cs");

    status = SSLNewContext(cred->credential_use == SECPKG_CRED_INBOUND, &s->context);
    if (status != noErr)
    {
        ERR("Failed to create session context: %d\n", status);
        goto fail;
    }

    /* We don't handle all the signature algorithms that OS X supports so it is important
     * to limit the enabled ciphers before the handshake happens. That will ensure we
     * only get certificates we can handle (assuming the server has alternate chains to offer).
     */
    status = SSLSetEnabledCiphers(s->context, enabled_cipher_suites, sizeof(enabled_cipher_suites) / sizeof(SSLCipherSuite));
    if (status != noErr)
    {
        ERR("Failed to set enabled ciphers: %d\n", status);
        goto fail;
    }

    status = SSLSetConnection(s->context, s);
    if (status != noErr)
    {
        ERR("Failed to set session connection: %d\n", status);
        goto fail;
    }

    status = SSLSetEnableCertVerify(s->context, FALSE);
    if (status != noErr)
    {
        ERR("Failed to disable certificate verification: %d\n", status);
        goto fail;
    }

    for(i=0; i < sizeof(protocol_priority_flags)/sizeof(*protocol_priority_flags); i++) {
        if(!(protocol_priority_flags[i].enable_flag & supported_protocols))
           continue;

        status = SSLSetProtocolVersionEnabled(s->context, protocol_priority_flags[i].mac_version,
                (cred->enabled_protocols & protocol_priority_flags[i].enable_flag) != 0);
        if (status != noErr)
        {
            ERR("Failed to set SSL version %d: %d\n", protocol_priority_flags[i].mac_version, status);
            goto fail;
        }
    }

    status = SSLSetIOFuncs(s->context, schan_pull_adapter, schan_push_adapter);
    if (status != noErr)
    {
        ERR("Failed to set session I/O funcs: %d\n", status);
        goto fail;
    }

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

    *session = (schan_imp_session)s;
    return TRUE;

fail:
    heap_free(s);
    return FALSE;
}
Exemple #4
0
static void
ssl_cdsa_create_context(gpointer data) {
    PurpleSslConnection *gsc = (PurpleSslConnection *)data;
    PurpleAccount *account = gsc->account;
	PurpleSslCDSAData *cdsa_data;
    OSStatus err;
	bool requireFS = purple_account_get_bool(account, "require_forward_secrecy", FALSE);
    
    /*
	 * allocate some memory to store variables for the cdsa connection.
	 * the memory comes zero'd from g_new0 so we don't need to null the
	 * pointers held in this struct.
	 */
    cdsa_data = g_new0(PurpleSslCDSAData, 1);
	gsc->private_data = cdsa_data;
	connections = g_list_append(connections, gsc);
    
    /*
	 * allocate a new SSLContextRef object
	 */
    err = SSLNewContext(false, &cdsa_data->ssl_ctx);
	if (err != noErr) {
		purple_debug_error("cdsa", "SSLNewContext failed\n");
		if (gsc->error_cb != NULL)
			gsc->error_cb(gsc, PURPLE_SSL_HANDSHAKE_FAILED,
                          gsc->connect_cb_data);
        
		purple_ssl_close(gsc);
		return;
	}
    
    /*
     * Set up our callbacks for reading/writing the file descriptor
     */
    err = SSLSetIOFuncs(cdsa_data->ssl_ctx, SocketRead, SocketWrite);
    if (err != noErr) {
		purple_debug_error("cdsa", "SSLSetIOFuncs failed\n");
		if (gsc->error_cb != NULL)
			gsc->error_cb(gsc, PURPLE_SSL_HANDSHAKE_FAILED,
                          gsc->connect_cb_data);
        
		purple_ssl_close(gsc);
		return;
    }
    
    /*
     * Pass the connection information to the connection to be used by our callbacks
     */
    err = SSLSetConnection(cdsa_data->ssl_ctx, (SSLConnectionRef)(intptr_t)gsc->fd);
    if (err != noErr) {
		purple_debug_error("cdsa", "SSLSetConnection failed: %d\n", err);
		if (gsc->error_cb != NULL)
			gsc->error_cb(gsc, PURPLE_SSL_HANDSHAKE_FAILED,
                          gsc->connect_cb_data);
        
		purple_ssl_close(gsc);
		return;
    }
	
	size_t numCiphers = 0;
	
	err = SSLGetNumberEnabledCiphers(cdsa_data->ssl_ctx, &numCiphers);
	
	if (err != noErr) {
		purple_debug_error("cdsa", "SSLGetNumberEnabledCiphers failed: %d\n", err);
        if (gsc->error_cb != NULL)
            gsc->error_cb(gsc, PURPLE_SSL_HANDSHAKE_FAILED,
                          gsc->connect_cb_data);
        
        purple_ssl_close(gsc);
        return;
	}
	
	SSLCipherSuite ciphers[numCiphers];
    
    err = SSLGetEnabledCiphers(cdsa_data->ssl_ctx, ciphers, &numCiphers);
	if (err != noErr) {
		purple_debug_error("cdsa", "SSLGetSupportedCiphers failed: %d\n", err);
        if (gsc->error_cb != NULL)
            gsc->error_cb(gsc, PURPLE_SSL_HANDSHAKE_FAILED,
                          gsc->connect_cb_data);
        
        purple_ssl_close(gsc);
        return;
	}
	
	SSLCipherSuite enabledCiphers[numCiphers];
	size_t numEnabledCiphers = 0;
	int i;
	
	for (i = 0; i < numCiphers; i++) {
		if (ssl_cdsa_use_cipher(ciphers[i], requireFS)) {
			enabledCiphers[numEnabledCiphers] = ciphers[i];
			numEnabledCiphers++;
		}
	}
	
    err = SSLSetEnabledCiphers(cdsa_data->ssl_ctx, enabledCiphers, numEnabledCiphers);
    if (err != noErr) {
        purple_debug_error("cdsa", "SSLSetEnabledCiphers failed: %d\n", err);
        if (gsc->error_cb != NULL)
            gsc->error_cb(gsc, PURPLE_SSL_HANDSHAKE_FAILED,
                          gsc->connect_cb_data);
        
        purple_ssl_close(gsc);
        return;
    }
    
    if (purple_account_get_bool(account, PURPLE_SSL_CDSA_BUGGY_TLS_WORKAROUND, false)) {
        purple_debug_info("cdsa", "Explicitly disabling TLS 1.1 and above to try and work around buggy TLS stacks\n");
        
        OSStatus protoErr;
        protoErr = SSLSetProtocolVersionEnabled(cdsa_data->ssl_ctx, kSSLProtocolAll, false);
        if (protoErr != noErr) {
            purple_debug_error("cdsa", "SSLSetProtocolVersionEnabled failed to disable protocols\n");
            if (gsc->error_cb != NULL)
                gsc->error_cb(gsc, PURPLE_SSL_HANDSHAKE_FAILED, gsc->connect_cb_data);
            purple_ssl_close(gsc);
            return;
        }
        
        protoErr = SSLSetProtocolVersionEnabled(cdsa_data->ssl_ctx, kSSLProtocol3, true);
        protoErr = SSLSetProtocolVersionEnabled(cdsa_data->ssl_ctx, kTLSProtocol1, true);
    }
    
    if(gsc->host) {
        /*
         * Set the peer's domain name so CDSA can check the certificate's CN
         */
        err = SSLSetPeerDomainName(cdsa_data->ssl_ctx, gsc->host, strlen(gsc->host));
        if (err != noErr) {
            purple_debug_error("cdsa", "SSLSetPeerDomainName failed\n");
            if (gsc->error_cb != NULL)
                gsc->error_cb(gsc, PURPLE_SSL_HANDSHAKE_FAILED,
                              gsc->connect_cb_data);
            
            purple_ssl_close(gsc);
            return;
        }
    }
    
	/*
     * Disable verifying the certificate chain.
	 * We have to do that manually later on! This is the only way to be able to continue with a connection, even though the user
	 * had to manually accept the certificate.
     */
	err = SSLSetEnableCertVerify(cdsa_data->ssl_ctx, false);
    if (err != noErr) {
		purple_debug_error("cdsa", "SSLSetEnableCertVerify failed\n");
        /* error is not fatal */
    }
	
	cdsa_data->handshake_handler = purple_input_add(gsc->fd, PURPLE_INPUT_READ, ssl_cdsa_handshake_cb, gsc);
}
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;
}