tortoiseSVN切换登录账号失败【已解决】

发布于:2025-04-19 ⋅ 阅读:(15) ⋅ 点赞:(0)

问题描述

使用同一个账号登录svn。有的电脑清除登录信息后,执行SVN-log命令,可直接查看log等一系列操作不需要登录,断网重启电脑、卸载重新安装都不起作用。

有的电脑清除登录信息,执行SVN-log命令后会弹出登录对话框。

在网上没有找到解决方法。

从源代码分析

使用debug方式编译代码,后面使用vs调试。

编译前,修改ext\serf\serf_private.h文件:

将上述几个宏都设置为1,方便调试查看serf模块输出的log。执行以下命令编译。

nant x64 debug setup

编译完成后,用设置VS的TortoiseProc项目的属性

这样TortoiseProc运行时,会出现一个控制台用于输出serf模块的运行log。

A.设置界面,清除登录信息

CSetSavedDataPage::OnInitDialog

保存界面初始化

点击“Clear All”

CSetSavedDataPage::OnBnClickedAuthhistclear

清除注册表\HKEY_CURRENT_USER\SOFTWARE\TortoiseSVN\Auth

删除文件夹C:\Users\Administrator\AppData\Roaming\Subversion\auth\

清除注册表\HKEY_CURRENT_USER\SOFTWARE\TortoiseSVN\CAPIAuthz

B.清除登录信息后能直接使用svn的电脑上执行svn-log命令

LogCommand::Execute=>

CLogDlg dlg;

dlg.DoModal();=>

CLogDlg::OnInitDialog()=>

ReadProjectPropertiesAndBugTraqInfo=>m_ProjectProperties.ReadProps(m_path);

new async::CAsyncCall(this, &CLogDlg::LogThread, &netScheduler)

new async::CAsyncCall(this, &CLogDlg::DetectVisualStudioRunningThread, &vsRunningScheduler);

CLogDlg::LogThread

new async::CAsyncCall(this, &CLogDlg::StatusThread, &diskScheduler);

GetRootAndHead(m_path, rootpath, head)=>

+svnPath = path.GetSVNApiPath(localpool)

+cachedProperties.GetHeadRevision (uuid, path) =>

++svn.GetHEADRevision (url, false)=>

+++svn_client_open_ra_session2=>

+4+svn_client__open_ra_session_internal=>

+5+SVN_ERR(svn_ra_create_callbacks(&cbtable, result_pool)) //设置了数个回调函数

+5+SVN_ERR(svn_ra_open4  =>

+6+SVN_ERR(svn_auth__make_session_auth( =>

+7+SVN_ERR(initfunc(svn_ra_version(), &vtable, scratch_pool)) => //vtable为serf模块的所有函数列表

+8+svn_ra_serf__init

+6+session = apr_pcalloc(sesspool, sizeof(*session));

+6+session->cancel_func = callbacks->cancel_func;//callbacks->cancel_func = 0x00007ffbc42c31e0 {libsvn_tsvn.dll!cancel_callback(void *)}

+6+err = vtable->open_session =>  //svn_ra_serf__open

+7+serf_sess = apr_pcalloc(result_pool, sizeof(*serf_sess));

+7+serf_sess->wc_callbacks = callbacks; //svn模块的回调函数s

+7+serf_sess->progress_func = callbacks->progress_func; //callbacks->progress_func = 0x00007ffc732e3710 {libsvn_tsvn.dll!progress_func(__int64, __int64, void *, apr_pool_t *)}

+7+serf_sess->cancel_func = callbacks->cancel_func;

//callbacks->cancel_func = 0x00007ffc732e31e0 {libsvn_tsvn.dll!cancel_callback(void *)}

+7+serf_connection_create2(

+7+serf_context_set_progress_cb(serf_sess->context, svn_ra_serf__progress, // progress_func = 0x00007ffc733ee8e0 {libsvn_tsvn.dll!svn_ra_serf__progress(void *, __int64, __int64)}

+7+err = svn_ra_serf__exchange_capabilities(serf_sess, corrected_url, =>

+8+svn_ra_serf__context_run_one =>

+9+svn_ra_serf__context_run_wait =>

+A+svn_ra_serf__context_run =>

+B+serf_context_run =>

+C+serf_event_trigger =>

+D+serf__process_connection =>

+E+read_from_connection  =>

+F+serf_bucket_peek => //serf_ssl_peek

+G+serf_databuf_peek =>

+H+common_databuf_prep =>

+I+status = (*databuf->read)(databuf->read_baton, sizeof(databuf->buf), => //ssl_decrypt

+J+ssl23_read =>

+K+ssl23_connect =>

+L+ssl23_get_server_hello =>

+M+ssl3_connect =>

+N+ssl3_get_server_certificate =>

+O+ssl_verify_cert_chain

...

调用层次有点多,不再继续跟踪。。

查找证书相关信息的代码

windows_ssl_server_trust_first_credentials(void **credentials,

                                           void **iter_baton,

                                           void *provider_baton,

                                           apr_hash_t *parameters,

                                           const char *realmstring,

                                           apr_pool_t *pool)

{

  apr_uint32_t *failure_ptr = svn_hash_gets(parameters,

SVN_AUTH_PARAM_SSL_SERVER_FAILURES);

  apr_uint32_t failures = *failure_ptr;

  const svn_auth_ssl_server_cert_info_t *cert_info =

    svn_hash_gets(parameters, SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO);

证书获取

ssl_verify_cert_chain(SSL *s, STACK_OF(X509) *sk) =>

X509_verify_cert(X509_STORE_CTX *ctx) =>

validate_server_certificate(int cert_valid, X509_STORE_CTX *store_ctx) =>

server_cert = X509_STORE_CTX_get_current_cert(store_ctx);

        cert = apr_palloc(subpool, sizeof(serf_ssl_certificate_t));

        cert->ssl_cert = server_cert;

        cert->depth = depth;

ssl_server_cert_cb(void *baton, int failures,

                const serf_ssl_certificate_t *cert)  =>

ssl_server_cert(void *baton, int failures,

                const serf_ssl_certificate_t *cert,

                apr_pool_t *scratch_pool)

  serf_cert = serf_ssl_cert_certificate(cert, scratch_pool);

cert_info.hostname = svn_hash_gets(subject, "CN");

  cert_info.fingerprint = svn_hash_gets(serf_cert, "sha1");

  if (! cert_info.fingerprint)

    cert_info.fingerprint = apr_pstrdup(scratch_pool, "<unknown>");

  cert_info.valid_from = svn_hash_gets(serf_cert, "notBefore");

  if (! cert_info.valid_from)

    cert_info.valid_from = apr_pstrdup(scratch_pool, "[invalid date]");

  cert_info.valid_until = svn_hash_gets(serf_cert, "notAfter");

if (! cert_info.valid_until)

    cert_info.valid_until = apr_pstrdup(scratch_pool, "[invalid date]");

  cert_info.issuer_dname = convert_organisation_to_str(issuer, scratch_pool);

  cert_info.ascii_cert = serf_ssl_cert_export(cert, scratch_pool);

svn_auth_set_parameter(conn->session->auth_baton,

                         SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO,

                         &cert_info);

对代码不太熟悉,进展困难,放弃,换个方向

C.清除登录信息后能需要登录后才能使用svn的电脑上执行svn-log命令

弹出登录对话框时的堆栈调用情况

定位代码:

serf__handle_basic_auth(int code, =>

status = serf__provide_credentials(ctx,

   &username, &password,

   request, baton,

   code, authn_info->scheme->name,

   realm, cred_pool);

serf__provide_credentials(serf_context_t *ctx, =>

/* Ask the application. */

status = (*ctx->cred_cb)(username, password,

 authn_req, authn_req->handler_baton,

 code, authn_type, realm, pool);

svn_auth_first_credentials(void **credentials, =>

    for (i = 0; i < table->providers->nelts; i++)

        {

          provider = APR_ARRAY_IDX(table->providers, i,

                                   svn_auth_provider_object_t *);

          SVN_ERR(provider->vtable->first_credentials(&creds, &iter_baton,

provider->provider_baton,

                                                      parameters,

                                                      realmstring,

                                                      auth_baton->pool));

          if (creds != NULL)

            {

              got_first = TRUE;

              break;

            }

simple_prompt_first_creds(void **credentials_p, =>

SVN_ERR(prompt_for_simple_creds((svn_auth_cred_simple_t **) credentials_p,

prompt_for_simple_creds(svn_auth_cred_simple_t **cred_p, =>

      SVN_ERR(pb->prompt_func(cred_p, pb->prompt_baton, realmstring,

                              default_username, may_save, pool));

SVNPrompt::simpleprompt(svn_auth_cred_simple_t **cred =>

if (!svn->m_bSuppressed && svn->SimplePrompt(UserName, PassWord, Realm, may_save))

{

    ret->username = apr_pstrdup(pool, CUnicodeUtils::GetUTF8(UserName));

    ret->password = apr_pstrdup(pool, CUnicodeUtils::GetUTF8(PassWord));

    ret->may_save = may_save;

    *cred = ret;

handle_auth_headers(int code, =>

status = handler(code, request, response,

 auth_hdr, auth_attr, baton, ctx->pool);

D.回到能直接登录的电脑,调试上述代码

发现步骤C处获取用户输入用户名和密码的代码未被调用,直接原因是handler 没有serf__provide_credentials的指针

serf_authn_schemes:

name = 0x00007ffbefe93b40 "Negotiate"

name = 0x00007ffbefe93b5c "NTLM"

name = 0x00007ffbefe93b6c "Digest"

name = 0x00007ffbefe93b7c "Basic"

进一步查阅SVN的资料发现:

SVN有多种身份认证的方式,需要用户名和密码登录的是“Basic Authentication”。认证方式可在SVN服务器上配置。

详细内容查看:

Version Control with Subversion

本文使用的SVN服务器是VisualSVN。

Serf模块按照优先级顺序,从左到右Negotiate、NTLM、Digest、Basic认证用户信息。

根据serf输出的log可以查看细节

无需输入用户名和密码的svn输出信息

客户端请求svn 路径信息:

服务器返回:

客户端再次请求(请求头有身份认证信息)

服务器返回

客户端继续请求(请求头有身份认证信息)

服务器返回

需要用户名和密码的svn输出信息

和“无需输入用户名和密码”步骤一致,最后一步服务器反馈:SAMEORIGIN认证失败

后续客户端使用NTLM认证仍失败。

最后使用Basic方式认证成功。

前两种认证方式失败的原因(涉及到客户端SSP、服务器)不再继续调查。

Microsoft Kerberos - Win32 apps | Microsoft Learn

Microsoft NTLM - Win32 apps | Microsoft Learn

解决方法

如果想切换账号,更改Windows登录的方式(如更换域账号)即可。或者将服务器的认证方式改成Basic Authentication单一认证(不建议)。


网站公告

今日签到

点亮在社区的每一天
去签到