Due to the needs of daily work, the unit used Serv-U to set up an FTP server. However, after taking over, it was discovered that there was a very serious problem. This FTP server was open to the public, and many users did not set passwords. If it is mandatory for everyone to set a password and it must be set on the server, doesn't it mean that everyone must tell the administrator their password? After all, many people are used to using the same password. What to do? The best way is of course to provide a Web page to provide the password modification function.
Just go ahead and check it online. One method is to use the ODBC function provided by Serv-U itself, use the database to store passwords, and directly operate the database to realize the password modification function. However, after testing this method Not very feasible. Because this FTP server has been running for a year and has nearly 60 users in it, the probability of errors when transplanting these users from the INI file to the database is still relatively high. It is easier to operate the INI file directly. .
The first thing is to figure out how Serv-U's user information is saved in the INI file and how the password is encrypted. The structure of the INI file is relatively simple. To modify the password, just find the section starting with [User=@UserID|1] and modify the value of the Password key under it. @UserID refers to the user's login ID.
1[GLOBAL]
2Version=6.1.0.5
3PacketTimeOut=300
4
5
6
7[Domain1]
8User1=
9User2=
10User3=
11
12
13
14[USER=abc|1]
15Password=niE383DC3710266ECAE04A6B3A18A2966D
16HomeDir=D:
17AlwaysAllowLogin=1
18ChangePassword=1
19TimeOut=600
20Note1="Wizard generated account"
21Access1=D:
twenty two
twenty three
The encryption method of user password can be found in the knowledge base of Ser-U official website
http://rhinosoft.com/KBArticle.asp?RefNo=1177&prod=su
Manually Entering Encrypted Passwords into the ServUDaemon.ini File
To generate an encrypted password, first two random characters (the 'salt' - in the range a..z, A..Z) are added to the beginning of the clear-text password. This is then hashed using MD5 and the resulting hash is hex-encoded. The result of this is written as plain-text starting with the 2 salt characters followed by the hex-encoded hash.
For a user account in the .ini file, this will look like:
Password=cb644FB1F31184F8D3D169B54B3D46AB1A
The salt is the string "cb", the MD5 hash is "644FB1F31184F8D3D169B54B3D46AB1A".
When verifying a user's password, Serv-U will do the same. It parses the salt from the user's stored password (ie. "cb" in this case), prepends it the password the user sent to it by the client, MD5 hashes it, and compares the result with the stored hash. If the values are equal, then the entered password is correct.
The encryption method is to randomly generate two letters, then splice the letters and the password, and then find their MD5 values. Finally, put the random letters in front of the MD5 value to get the encrypted password.
Next, you can write a program based on the above analysis to implement online modification.
1 /**//// <summary>
2 /// Get the MD5 value of the specified string
3 /// </summary>
4 /// <param name="strContent"></param>
5 /// <returns></returns>
6 public String MD5(String strContent)
7 {
8 System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
9 byte[] bytes = System.Text.Encoding.UTF8.GetBytes( strContent );
10 bytes = md5.ComputeHash( bytes );
11 md5.Clear();
12 string ret = "";
13 for(int i=0; i<bytes.Length; i++)
14 {
15 ret += Convert.ToString(bytes[i],16).PadLeft(2,'0');
16}
17 return ret.PadLeft(32,'0').ToUpper();
18}
19
20
21 /**//// <summary>
22 /// Generate a random string, the length of the string is 2
23 /// </summary>
24 /// <returns></returns>
25 public string GetRandomString()
26 {
27 string strReturn = "";
28 Random ran = new Random();
29 strReturn += Convert.ToChar( ran.Next( 26 ) + 'a' ).ToString();
30 strReturn += Convert.ToChar( ran.Next( 26 ) + 'a' ).ToString();
31 return strReturn;
32}
33
34 //Generate an encrypted password from the specified random letters and login password
35 public string CreateCryPassword( string strFrontChars, string strPassword)
36 {
37 return strFrontChars + MD5( strFrontChars + strPassword ).ToUpper().Trim();
38 }
39
40 /**//// <summary>
41 /// Click event of "Change Password", in which the password is modified.
42 /// </summary>
43 /// <param name="sender"></param>
44 /// <param name="e"></param>
45 private void btnModifyPwd_Click(object sender, System.EventArgs e)
46 {
47 string strUserID = txtLoginID.Text;
48 if(strUserID == String.Empty)
49 {
50 controlMessage.InnerHtml = "Username cannot be empty";
51 return;
52 }
53
54 //Determine whether the two password inputs are the same
55 if( txtNewPassword.Text != txtConfirmPassword.Text )
56 {
57 controlMessage.InnerHtml = "The passwords entered twice are inconsistent, please re-enter";
58 return;
59 }
60
61 IniFile ini = new IniFile( _strServUDaemonPath );
62 string strSectionValue = "USER=" + strUserID.Trim() + "|1";
63
64 //Determine whether the user exists by reading the HomeDir of the specified user
65 if( ini.ReadString( strSectionValue, "HomeDir", "" ) == "" )
66 {
67 controlMessage.InnerHtml = "The specified user does not exist";
68 return;
69 }
70
71 //Start to determine whether the password is correct
72 string strPassword = ini.ReadString( strSectionValue, "Password", "" );
73
74 string strPasswordFrontTwoChars;
75 bool bPasswordRight = false;
76 if(strPassword.Length > 2)
77 {
78 //Read the random letters contained in the password
79 strPasswordFrontTwoChars = strPassword.Substring(0, 2);
80 if( CreateCryPassword( strPasswordFrontTwoChars, txtOldPassword.Text ) == strPassword )
81 {//Password matches
82 bPasswordRight = true;
83}
84 else
85 {//Password does not match
86 bPasswordRight = false;
87 }
88}
89 else if( strPassword == txtOldPassword.Text) //The original password is empty
90 {
91 bPasswordRight = true;
92 }
93 else
94 {
95 bPasswordRight = false;
96 }
97
98 if( bPasswordRight )
99 {
100 //The password is correct, write a new password, and set up to automatically load new settings so that they will still be valid the next time they are changed.
101 ini.WriteString( strSectionValue, "Password", CreateCryPassword( GetRandomString(), txtNewPassword.Text ) );
102 controlMessage.InnerHtml = "Password change completed";
103}
104 else
105 {
106 controlMessage.InnerHtml = "Original password is wrong";
107 }
108
109 }
The _strServUDaemonPath variable in the above code is used to save the path where the ServUDaemon.ini file is located. This value can be obtained through the Web.Config setting in the PageLoad event.
But it didn't end there. After testing, it was found that there is a serious problem: after changing the password, only restarting Serv-U can the changed password take effect. Doesn't that mean it's useless? The administrator can't always restart the server to make the password change take effect.
Returning to Serv-U's official knowledge base again, I found the following content:
Manually Updating the ServUDaemon.ini File
Whenever changes are made directly to the ServUDaemon.ini file, add the following line under the Global area in the INI file.
ReloadSettings=True
Serv-U regularly checks the INI file for this setting. If it is present, Serv-U will refresh all stored settings for every domain on the server. This allows Serv-U to recognize the changes without having to be restarted.
After Serv-U loads the changes, it removes the "ReloadSettings=True" entry. This allows you to enter it again next time any changes are made.
In other words, as long as you add the key ReloadSettings in the GLOBAL section of the INI file and set its value to True, you can automatically update the password after changing it. So just modify the original code and insert the following code between lines 101 and 102:
ini.WriteString( "GLOBAL", "ReloadSettings", "True" );
At this point, a web page for changing the Serv-U password online is complete.
IniFile in the program is a class that encapsulates the API's operation on INI files. It only needs to implement reading and writing of strings.