Skip to content

Commit 9e6c625

Browse files
committed
fix(users): harden password hash handling in ModifyPasswd
Validate password crypt hashes before invoking chpasswd to reject invalid characters and malformed input per crypt(5). Also improve process isolation and sensitive data handling by clearing the child environment, switching to an explicit stdin pipe flow, and zeroing the temporary password buffer after use. Additionally, avoid exposing detailed backend errors to callers to reduce information disclosure risks. Signed-off-by: ComixHe <heyuming@deepin.org>
1 parent 9befeaa commit 9e6c625

1 file changed

Lines changed: 58 additions & 6 deletions

File tree

accounts1/users/prop.go

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,25 +157,77 @@ func ModifyShell(shell, username string) error {
157157
return doAction(userCmdModify, []string{"-s", shell, username})
158158
}
159159

160+
// from: https://manpages.debian.org/unstable/libcrypt-dev/crypt.5.en.html
161+
func IsValidCryptHash(hash string) bool {
162+
if hash == "" {
163+
return false
164+
}
165+
166+
for i := 0; i < len(hash); i++ {
167+
b := hash[i]
168+
169+
if b < 32 || b > 126 {
170+
return false
171+
}
172+
173+
switch b {
174+
case ' ', ':', ';', '*', '!', '\\':
175+
return false
176+
}
177+
}
178+
179+
return true
180+
}
181+
160182
func ModifyPasswd(words, username string) error {
161-
if len(words) == 0 {
183+
if words == "" || username == "" {
162184
return errInvalidParam
163185
}
164186
// 防止命令注入
165187
if strings.ContainsAny(words, "\n\r") || strings.ContainsAny(username, "\n\r:") {
166188
return errInvalidParam
167189
}
168190

191+
if !IsValidCryptHash(words) {
192+
return errors.New("invalid password hash")
193+
}
194+
169195
cmd := exec.Command(pwdCmdModify, "-e")
170-
input := fmt.Sprintf("%s:%s\n", username, words)
171-
cmd.Stdin = bytes.NewBufferString(input)
196+
cmd.Env = []string{}
197+
198+
stdin, err := cmd.StdinPipe()
199+
if err != nil {
200+
return fmt.Errorf("failed to create stdin pipe: %w", err)
201+
}
172202

173203
var stderr bytes.Buffer
174204
cmd.Stderr = &stderr
175205

176-
err := cmd.Run()
177-
if err != nil {
178-
return fmt.Errorf("failed to modify password: %v, %s", err, stderr.String())
206+
if err := cmd.Start(); err != nil {
207+
return fmt.Errorf("failed to start command: %w", err)
208+
}
209+
210+
// Write the password hash to stdin
211+
input := []byte(username + ":" + words + "\n")
212+
_, writeErr := stdin.Write(input)
213+
for i := range input {
214+
input[i] = 0
215+
}
216+
217+
if len(input) > 0 && input[0] != 0 {
218+
_ = input[0] // forbid DCE and ensure input is zeroed out
219+
}
220+
221+
stdin.Close()
222+
223+
if writeErr != nil {
224+
_ = cmd.Process.Kill()
225+
return fmt.Errorf("failed to write to stdin: %w", writeErr)
226+
}
227+
228+
if err := cmd.Wait(); err != nil {
229+
230+
return errors.New("failed to update system password configuration")
179231
}
180232

181233
return nil

0 commit comments

Comments
 (0)