/* * Copyright 2013 Couchbase, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "cbsasl/cbsasl.h" #include "cram-md5/hmac.h" #include "util.h" #include #include #include CBSASL_PUBLIC_API cbsasl_error_t cbsasl_client_new(const char *service, const char *serverFQDN, const char *iplocalport, const char *ipremoteport, const cbsasl_callback_t *prompt_supp, unsigned flags, cbsasl_conn_t **pconn) { cbsasl_conn_t *conn; cbsasl_callback_t *callbacks = (cbsasl_callback_t*)prompt_supp; int ii; if (prompt_supp == NULL) { return SASL_BADPARAM; } conn = calloc(1, sizeof(*conn)); if (conn == NULL) { return SASL_NOMEM; } conn->client = 1; ii = 0; /* Locate the callbacks */ while (callbacks[ii].id != CBSASL_CB_LIST_END) { if (callbacks[ii].id == CBSASL_CB_USER || callbacks[ii].id == CBSASL_CB_AUTHNAME) { union { int (*get)(void *, int, const char **, unsigned int *); int (*proc)(void); } hack; hack.proc = callbacks[ii].proc; conn->c.client.get_username = hack.get; conn->c.client.get_username_ctx = callbacks[ii].context; } else if (callbacks[ii].id == CBSASL_CB_PASS) { union { int (*get)(cbsasl_conn_t *, void *, int, cbsasl_secret_t **); int (*proc)(void); } hack; hack.proc = callbacks[ii].proc; conn->c.client.get_password = hack.get; conn->c.client.get_password_ctx = callbacks[ii].context; } ++ii; } if (conn->c.client.get_username == NULL || conn->c.client.get_password == NULL) { cbsasl_dispose(&conn); return SASL_NOUSER; } *pconn = conn; (void)service; (void)serverFQDN; (void)iplocalport; (void)ipremoteport; (void)flags; return SASL_OK; } CBSASL_PUBLIC_API cbsasl_error_t cbsasl_client_start(cbsasl_conn_t *conn, const char *mechlist, void **prompt_need, const char **clientout, unsigned int *clientoutlen, const char **mech) { if (conn->client == 0) { return SASL_BADPARAM; } if (strstr(mechlist, "CRAM-MD5") == NULL) { if (strstr(mechlist, "PLAIN") == NULL) { return SASL_NOMECH; } *mech = "PLAIN"; conn->c.client.plain = 1; } else { *mech = "CRAM-MD5"; conn->c.client.plain = 0; } if (conn->c.client.plain) { const char *usernm = NULL; unsigned int usernmlen; cbsasl_secret_t *pass; cbsasl_error_t ret; ret = conn->c.client.get_username(conn->c.client.get_username_ctx, CBSASL_CB_USER, &usernm, &usernmlen); if (ret != SASL_OK) { return ret; } ret = conn->c.client.get_password(conn, conn->c.client.get_password_ctx, CBSASL_CB_PASS, &pass); if (ret != SASL_OK) { return ret; } conn->c.client.userdata = calloc(usernmlen + 1 + pass->len + 1, 1); if (conn->c.client.userdata == NULL) { return SASL_NOMEM; } memcpy(conn->c.client.userdata + 1, usernm, usernmlen); memcpy(conn->c.client.userdata + usernmlen + 2, pass->data, pass->len); *clientout = conn->c.client.userdata; *clientoutlen = (unsigned int)(usernmlen + 2 + pass->len); } else { /* CRAM-MD5 */ *clientout = NULL; *clientoutlen = 0; } (void)prompt_need; return SASL_OK; } CBSASL_PUBLIC_API cbsasl_error_t cbsasl_client_step(cbsasl_conn_t *conn, const char *serverin, unsigned int serverinlen, void **not_used, const char **clientout, unsigned int *clientoutlen) { unsigned char digest[DIGEST_LENGTH]; char md5string[DIGEST_LENGTH * 2]; const char *usernm = NULL; unsigned int usernmlen; cbsasl_secret_t *pass; cbsasl_error_t ret; (void)not_used; if (conn->client == 0) { return SASL_BADPARAM; } if (conn->c.client.plain) { /* Shouldn't be called during plain auth */ return SASL_BADPARAM; } ret = conn->c.client.get_username(conn->c.client.get_username_ctx, CBSASL_CB_USER, &usernm, &usernmlen); if (ret != SASL_OK) { return ret; } ret = conn->c.client.get_password(conn, conn->c.client.get_password_ctx, CBSASL_CB_PASS, &pass); if (ret != SASL_OK) { return ret; } free(conn->c.client.userdata); conn->c.client.userdata = calloc(usernmlen + 1 + sizeof(md5string) + 1, 1); if (conn->c.client.userdata == NULL) { return SASL_NOMEM; } cbsasl_hmac_md5((unsigned char*)serverin, serverinlen, pass->data, pass->len, digest); cbsasl_hex_encode(md5string, (char *) digest, DIGEST_LENGTH); memcpy(conn->c.client.userdata, usernm, usernmlen); conn->c.client.userdata[usernmlen] = ' '; memcpy(conn->c.client.userdata + usernmlen + 1, md5string, sizeof(md5string)); *clientout = conn->c.client.userdata; *clientoutlen = strlen(conn->c.client.userdata); return SASL_CONTINUE; }