Skip to content

Fix user password change under FreeBSD

Background

Original https://gitlab.freedesktop.org/accountsservice/accountsservice doesn't fully work for FreeBSD. It runs Linux utilities to manange users/group/etc. Olivier Duchateau created patches to replace Linux utilities invocation with FreeBSD equivalents to fix it. Gleb Popov created this repo, a branch and incorporated those patches from Olivier for ease of management.

Issue

Current user password change code in freebsd branch has a bug. If one tries to change any user password via AccountsService the root password gets wiped out.

Steps to reproduce

#!/usr/local/bin/python3.11

import gi
gi.require_version('AccountsService', '1.0')
from gi.repository import AccountsService

USERNAME = "some_username"
PASSWORD = "some_password"
AccountsService.UserManager.get_default().list_users()
user = AccountsService.UserManager.get_default().get_user(USERNAME)
user.set_password(PASSWORD, "")

Root cause

In the original patch from Oliver Linux-specific chpasswd(8) tool was replaced with passwd(1) in user_change_password_authorized_cb() function. See 87cc02f3. This is incorrect since Linux's chpasswd -e expects username and password in the stdin in the form of partial entry from user database, like this one:

username:$6$zrCYXA63p70BPDnv$7jF1X5Gwyr1P4ijNWzpkyUfSZbznStsq9be3jqB/t.YXZW.VBc8JjW9GSYoe8IkOwOfvBvWWvl2fkxpwZE1u/0

While the passwd(1) wants just plaintext password. I. e. the username gets ommitted in such case. This is why current code affects only 'root' user as it's the user the tool runs on behalf of. Then the password gets wiped out because there is no retyping in stdin. So the sequence of events is:

  • accounts-daemon runs under 'root' user and gets a request via dbus to change some user's password
  • accounts-daemon spawns passwd(1) as root, i. e. no user is set as argument to the command
  • passwd(1) gets that partial entry from user database (see above) in stdin
  • passwd(1) asks to retype it to confirm both input are the same, but gets EOF in stdin
  • passwd(1) figures out both input do not match, starts from the beginning and asks to enter password
  • passwd(1) gets EOF in stdin, asks to retype it and gets EOF in stdin once more
  • root password is wiped out

Fix

The closest FreeBSD alternative to Lunux's chpass(8) is chpass(1). The difference is that chpass(1) needs the incoming data to be modified a bit and it doesn't support password input via stdin, only as argument. chpass(1) '-a' option wants a complete line from user database - with home dir, full name, shell, etc. So it's not easy to use it. There is '-p' that wants only encrypted password part of that line. And this seems like what is needed. But there is a better option in FreeBSD that does the same as chpass(1), but does support stdin for password input. It's pw(8). What's going on in this patch:

  • user_change_password_authorized_cb() function gets username:$6$zrCYXA63p70BPDnv$7jF1X5Gwyr1P4ijNWzpkyUfSZbznStsq9be3jqB/t.YXZW.VBc8JjW9G SYoe8IkOwOfvBvWWvl2fkxpwZE1u/0 as username/password data.
  • figure out where colon (':') is located in the input data
  • take pointer to the next char after the colon (':') to get where encrypted password string starts
  • feed encrypted password prepared in previous steps to pw usermod <username> -H 0 via stdin

Useful links

Edited by Pavel Timofeev

Merge request reports

Loading