Index: include/sasl.h =================================================================== RCS file: /cvs/src/sasl/include/sasl.h,v retrieving revision 1.43 diff -c -r1.43 sasl.h *** sasl.h 2001/03/24 01:47:28 1.43 --- sasl.h 2001/04/03 19:56:41 *************** *** 46,51 **** --- 46,52 ---- * sasl_server_start Begin an authentication exchange * sasl_server_step Perform one authentication exchange step * sasl_checkpass Check a plaintext passphrase + * sasl_checkapop Check an APOP challenge/response (optional) * sasl_userexists Check if user exists * sasl_setpass Change a password or add a user entry * *************** *** 868,873 **** --- 869,901 ---- LIBSASL_API int sasl_user_exists(const char *service, const char *user_realm, const char *user); + + /* check if an APOP exchange is valid + * (note this is an optional part of the SASL API) + * inputs: + * user -- user to query in current user_realm + * userlen -- length of username, 0 = strlen(user) + * challenge -- challenge which was sent to client + * challen -- length of challenge, 0 = strlen(challenge) + * response -- client response string: "32HEXDIGIT" + * resplen -- length of response, 0 = strlen(response) + * outputs: + * errstr -- set to error message for use in protocols + * returns + * SASL_OK -- success + * SASL_FAIL -- failure + * SASL_BADPARAM -- invalid parameter + * SASL_BADAUTH -- authentication failed + * SASL_NOUSER -- user not found + */ + LIBSASL_API int sasl_checkapop(sasl_conn_t *conn, + const char *user, + unsigned userlen, + const char *challenge, + unsigned challen, + const char *response, + unsigned resplen, + const char **errstr); /* set the password for a user * conn -- SASL connection Index: lib/checkpw.c =================================================================== RCS file: /cvs/src/sasl/lib/checkpw.c,v retrieving revision 1.41 diff -c -r1.41 checkpw.c *** checkpw.c 2001/03/29 22:00:06 1.41 --- checkpw.c 2001/04/03 19:56:44 *************** *** 761,766 **** --- 761,794 ---- return SASL_OK; } + /* we store the following secret to check APOP plaintext passwords: + * + * \0 + * + * where = (should we do encryption using salt?) + * + * NOTE: this is a hack until PLAIN secrets are stored as plaintext + */ + static int _sasl_make_apop_secret(const char *salt, + const char *passwd, int passlen, + sasl_secret_t **secret) + { + unsigned sec_len = 16 + 1 + passlen; /* salt + "\0" + passwd */ + + *secret = (sasl_secret_t *) sasl_ALLOC(sizeof(sasl_secret_t) + + sec_len * sizeof(char)); + if (*secret == NULL) { + return SASL_NOMEM; + } + + memcpy((*secret)->data, salt, 16); + memcpy((*secret)->data + 16, "\0", 1); + memcpy((*secret)->data + 17, passwd, passlen); + (*secret)->len = sec_len; + + return SASL_OK; + } + /* returns the realm we should pretend to be in */ static int parseuser(char **user, char **realm, const char *user_realm, const char *serverFQDN, const char *input) *************** *** 934,939 **** --- 962,978 ---- _sasl_log(conn, SASL_LOG_ERR, NULL, ret, 0, "failed to set plaintext secret for %s: %z", userid); } + if (ret == SASL_OK) { + sasl_rand(rpool, salt, 16); + ret = _sasl_make_apop_secret(salt, pass, passlen, &sec); + } + if (ret == SASL_OK) { + ret = putsec(context, "PLAIN-APOP", userid, realm, sec); + } + if (ret != SASL_OK) { + _sasl_log(conn, SASL_LOG_ERR, NULL, ret, 0, + "failed to set APOP secret for %s: %z", userid); + } if (rpool) { sasl_randfree(&rpool); } *************** *** 954,963 **** --- 993,1076 ---- _sasl_log(conn, SASL_LOG_ERR, NULL, ret, 0, "failed to disable account for %s: %z", userid); } + if (ret == SASL_OK) { + ret = putsec(context, "PLAIN-APOP", userid, realm, NULL); + } + if (ret != SASL_OK) { + _sasl_log(conn, SASL_LOG_ERR, NULL, ret, 0, + "failed to disable APOP account for %s: %z", userid); + } } if (userid) sasl_FREE(userid); if (realm) sasl_FREE(realm); + return ret; + } + + int _sasl_sasldb_verify_apop(sasl_conn_t *conn, + const char *userstr, + const char *challenge, + const char *response, + const char *user_realm, + const char **reply) + { + sasl_server_getsecret_t *getsec; + void *context = NULL; + int ret = SASL_FAIL; + sasl_secret_t *secret = NULL; + char *userid = NULL; + char *realm = NULL; + char *passwd; + unsigned char digest[16]; + char digeststr[32]; + MD5_CTX ctx; + int i; + + if (reply) { *reply = NULL; } + if (!userstr || !challenge || !response) { + return SASL_BADPARAM; + } + ret = parseuser(&userid, &realm, user_realm, conn->serverFQDN, userstr); + if (ret != SASL_OK) { + /* error parsing user */ + goto done; + } + ret = _sasl_getcallback(conn, SASL_CB_SERVER_GETSECRET, &getsec, &context); + if (ret != SASL_OK) { + /* error getting getsecret callback */ + goto done; + } + + ret = getsec(context, "PLAIN-APOP", userid, realm, &secret); + if (ret != SASL_OK) { + /* error getting APOP secret */ + goto done; + } + + passwd = secret->data + 17; /* salt + "\0" */ + + MD5Init(&ctx); + MD5Update(&ctx, challenge, strlen(challenge)); + MD5Update(&ctx, passwd, strlen(passwd)); + MD5Final(digest, &ctx); + + /* convert digest from binary to ASCII hex */ + for (i = 0; i < 16; i++) + sprintf(digeststr + (i*2), "%02x", digest[i]); + + if (!strncasecmp(digeststr, response, 32)) { + /* password verified! */ + ret = SASL_OK; + } else { + /* passwords do not match */ + ret = SASL_BADAUTH; + } + + done: + if (userid) sasl_FREE(userid); + if (realm) sasl_FREE(realm); + + if (secret) sasl_free_secret(&secret); return ret; } Index: lib/saslint.h =================================================================== RCS file: /cvs/src/sasl/lib/saslint.h,v retrieving revision 1.33 diff -c -r1.33 saslint.h *** saslint.h 2000/08/17 22:14:25 1.33 --- saslint.h 2001/04/03 19:56:44 *************** *** 212,215 **** --- 212,222 ---- int flags, const char **errstr); + int _sasl_sasldb_verify_apop(sasl_conn_t *conn, + const char *userstr, + const char *challenge, + const char *response, + const char *user_realm, + const char **reply); + #endif /* SASLINT_H */ Index: lib/server.c =================================================================== RCS file: /cvs/src/sasl/lib/server.c,v retrieving revision 1.84 diff -c -r1.84 server.c *** server.c 2001/02/06 20:54:28 1.84 --- server.c 2001/04/03 19:56:45 *************** *** 84,89 **** --- 84,90 ---- * sasl_server_start * sasl_server_step * sasl_checkpass + * sasl_checkapop * sasl_userexists <= not yet implemented * sasl_setpass */ *************** *** 1423,1428 **** --- 1424,1470 ---- _sasl_transition(conn, pass, passlen); } + + return result; + } + + /* check if an APOP exchange is valid + * (note this is an optional part of the SASL API) + * inputs: + * user -- user to query in current user_realm + * userlen -- length of username, 0 = strlen(user) + * challenge -- challenge which was sent to client + * challen -- length of challenge, 0 = strlen(challenge) + * response -- client response string: "32HEXDIGIT" + * resplen -- length of response, 0 = strlen(response) + * outputs: + * errstr -- set to error message for use in protocols + * returns + * SASL_OK -- success + * SASL_FAIL -- failure + * SASL_BADPARAM -- invalid parameter + * SASL_BADAUTH -- authentication failed + * SASL_NOUSER -- user not found + */ + int sasl_checkapop(sasl_conn_t *conn, + const char *user, + unsigned userlen __attribute__((unused)), + const char *challenge, + unsigned challen __attribute__((unused)), + const char *response, + unsigned resplen __attribute__((unused)), + const char **errstr) + { + sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn; + int result = SASL_FAIL; + + /* check params */ + if (_sasl_server_active==0) return SASL_FAIL; + if ((conn == NULL) || (user == NULL) || + (challenge == NULL) || (response == NULL)) return SASL_BADPARAM; + + result = _sasl_sasldb_verify_apop(conn, user, challenge, response, + s_conn->user_realm, errstr); return result; }