int auth_cram_md5_client( auth_instance *ablock, /* authenticator block */ smtp_inblock *inblock, /* input connection */ smtp_outblock *outblock, /* output connection */ int timeout, /* command timeout */ uschar *buffer, /* for reading response */ int buffsize) /* size of buffer */ { auth_cram_md5_options_block *ob = (auth_cram_md5_options_block *)(ablock->options_block); uschar *secret = expand_string(ob->client_secret); uschar *name = expand_string(ob->client_name); uschar *challenge, *p; int i; uschar digest[16]; /* If expansion of either the secret or the user name failed, return CANCELLED or ERROR, as approriate. */ if (!secret || !name) { if (expand_string_forcedfail) { *buffer = 0; /* No message */ return CANCELLED; } string_format(buffer, buffsize, "expansion of \"%s\" failed in " "%s authenticator: %s", !secret ? ob->client_secret : ob->client_name, ablock->name, expand_string_message); return ERROR; } /* Initiate the authentication exchange and read the challenge, which arrives in base 64. */ if (smtp_write_command(outblock, FALSE, "AUTH %s\r\n", ablock->public_name) < 0) return FAIL_SEND; if (!smtp_read_response(inblock, buffer, buffsize, '3', timeout)) return FAIL; if (b64decode(buffer + 4, &challenge) < 0) { string_format(buffer, buffsize, "bad base 64 string in challenge: %s", big_buffer + 4); return ERROR; } /* Run the CRAM-MD5 algorithm on the secret and the challenge */ compute_cram_md5(secret, challenge, digest); /* Create the response from the user name plus the CRAM-MD5 digest */ string_format(big_buffer, big_buffer_size - 36, "%s", name); for (p = big_buffer; *p; ) p++; *p++ = ' '; for (i = 0; i < 16; i++) { sprintf(CS p, "%02x", digest[i]); p += 2; } /* Send the response, in base 64, and check the result. The response is in big_buffer, but b64encode() returns its result in working store, so calling smtp_write_command(), which uses big_buffer, is OK. */ buffer[0] = 0; if (smtp_write_command(outblock, FALSE, "%s\r\n", b64encode(big_buffer, p - big_buffer)) < 0) return FAIL_SEND; return smtp_read_response(inblock, (uschar *)buffer, buffsize, '2', timeout) ? OK : FAIL; }
int auth_spa_client( auth_instance *ablock, /* authenticator block */ smtp_inblock *inblock, /* connection inblock */ smtp_outblock *outblock, /* connection outblock */ int timeout, /* command timeout */ uschar *buffer, /* buffer for reading response */ int buffsize) /* size of buffer */ { auth_spa_options_block *ob = (auth_spa_options_block *)(ablock->options_block); SPAAuthRequest request; SPAAuthChallenge challenge; SPAAuthResponse response; char msgbuf[2048]; char *domain = NULL; char *username, *password; /* Code added by PH to expand the options */ *buffer = 0; /* Default no message when cancelled */ username = CS expand_string(ob->spa_username); if (username == NULL) { if (expand_string_forcedfail) return CANCELLED; string_format(buffer, buffsize, "expansion of \"%s\" failed in %s " "authenticator: %s", ob->spa_username, ablock->name, expand_string_message); return ERROR; } password = CS expand_string(ob->spa_password); if (password == NULL) { if (expand_string_forcedfail) return CANCELLED; string_format(buffer, buffsize, "expansion of \"%s\" failed in %s " "authenticator: %s", ob->spa_password, ablock->name, expand_string_message); return ERROR; } if (ob->spa_domain != NULL) { domain = CS expand_string(ob->spa_domain); if (domain == NULL) { if (expand_string_forcedfail) return CANCELLED; string_format(buffer, buffsize, "expansion of \"%s\" failed in %s " "authenticator: %s", ob->spa_domain, ablock->name, expand_string_message); return ERROR; } } /* Original code */ if (smtp_write_command(outblock, FALSE, "AUTH %s\r\n", ablock->public_name) < 0) return FAIL_SEND; /* wait for the 3XX OK message */ if (!smtp_read_response(inblock, (uschar *)buffer, buffsize, '3', timeout)) return FAIL; DSPA("\n\n%s authenticator: using domain %s\n\n", ablock->name, domain); spa_build_auth_request (&request, CS username, domain); spa_bits_to_base64 (US msgbuf, (unsigned char*)&request, spa_request_length(&request)); DSPA("\n\n%s authenticator: sending request (%s)\n\n", ablock->name, msgbuf); /* send the encrypted password */ if (smtp_write_command(outblock, FALSE, "%s\r\n", msgbuf) < 0) return FAIL_SEND; /* wait for the auth challenge */ if (!smtp_read_response(inblock, (uschar *)buffer, buffsize, '3', timeout)) return FAIL; /* convert the challenge into the challenge struct */ DSPA("\n\n%s authenticator: challenge (%s)\n\n", ablock->name, buffer + 4); spa_base64_to_bits ((char *)(&challenge), sizeof(challenge), (const char *)(buffer + 4)); spa_build_auth_response (&challenge, &response, CS username, CS password); spa_bits_to_base64 (US msgbuf, (unsigned char*)&response, spa_request_length(&response)); DSPA("\n\n%s authenticator: challenge response (%s)\n\n", ablock->name, msgbuf); /* send the challenge response */ if (smtp_write_command(outblock, FALSE, "%s\r\n", msgbuf) < 0) return FAIL_SEND; /* If we receive a success response from the server, authentication has succeeded. There may be more data to send, but is there any point in provoking an error here? */ if (smtp_read_response(inblock, US buffer, buffsize, '2', timeout)) return OK; /* Not a success response. If errno != 0 there is some kind of transmission error. Otherwise, check the response code in the buffer. If it starts with '3', more data is expected. */ if (errno != 0 || buffer[0] != '3') return FAIL; return FAIL; }