Skip to content

userdb: add birthDate field to JSON user records#40954

Merged
bluca merged 2 commits intosystemd:mainfrom
dylanmtaylor:add-birth-date-field
Mar 18, 2026
Merged

userdb: add birthDate field to JSON user records#40954
bluca merged 2 commits intosystemd:mainfrom
dylanmtaylor:add-birth-date-field

Conversation

@dylanmtaylor
Copy link
Copy Markdown
Contributor

@dylanmtaylor dylanmtaylor commented Mar 5, 2026

Stores the user's birth date for age verification, as required by recent laws
in California (AB-1043), Colorado (SB26-051), Brazil (Lei 15.211/2025), etc.

The xdg-desktop-portal project is adding an age verification portal
(flatpak/xdg-desktop-portal#1922) that needs a data source for the user's age.
userdb already stores personal metadata (emailAddress, realName, location)
so birthDate is a natural fit.

Full date rather than just birth year: birth year alone has up to ~12 months of
imprecision at age boundaries, which could misclassify a 17-year-old as 18 or
vice versa.

Authorization

birthDate is excluded from user_record_self_modifiable_fields(), so only
administrators can set or change it via homectl. The field remains in the
regular (non-privileged) JSON section, keeping it readable by the user and
applications (e.g. xdg-desktop-portal).

Related

@github-actions github-actions bot added documentation util-lib please-review PR is ready for (re-)review by a maintainer labels Mar 5, 2026
@YHNdnzj YHNdnzj added reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks userdb and removed please-review PR is ready for (re-)review by a maintainer labels Mar 5, 2026
@dylanmtaylor dylanmtaylor force-pushed the add-birth-date-field branch from 303e1c4 to 418cc85 Compare March 5, 2026 01:11
@github-actions github-actions bot added sysupdate please-review PR is ready for (re-)review by a maintainer and removed reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks labels Mar 5, 2026
@dylanmtaylor dylanmtaylor requested a review from YHNdnzj March 5, 2026 01:14
@dylanmtaylor dylanmtaylor force-pushed the add-birth-date-field branch from 418cc85 to e704ee3 Compare March 5, 2026 01:22
@dylanmtaylor dylanmtaylor requested a review from YHNdnzj March 5, 2026 01:23
@dylanmtaylor
Copy link
Copy Markdown
Contributor Author

The clang-tidy test failures appear to be pre-existing and don't seem to be related to my code. https://github.com/systemd/systemd/actions/runs/22697752278/job/65807875286?pr=40954

@dylanmtaylor dylanmtaylor force-pushed the add-birth-date-field branch from e704ee3 to 4110739 Compare March 5, 2026 02:21
@github-actions github-actions bot added the tests label Mar 5, 2026
@dylanmtaylor dylanmtaylor force-pushed the add-birth-date-field branch 2 times, most recently from a60422b to 573b071 Compare March 5, 2026 02:56
@poettering poettering removed the please-review PR is ready for (re-)review by a maintainer label Mar 5, 2026
@bluca
Copy link
Copy Markdown
Member

bluca commented Mar 9, 2026

Sorry but I do actually mind a lot, as getting this right is extremely important, and it cannot get kicked in the long grass, so please fix it here, as there's no rush anyway and there's all the time in the world as we have to do a release anyway right now - thanks

@bluca bluca added reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks and removed good-to-merge/after-next-release labels Mar 9, 2026
@dylanmtaylor dylanmtaylor force-pushed the add-birth-date-field branch from 51fad6d to 23c6113 Compare March 9, 2026 14:25
@github-actions github-actions bot added please-review PR is ready for (re-)review by a maintainer and removed reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks labels Mar 9, 2026
@dylanmtaylor
Copy link
Copy Markdown
Contributor Author

@bluca I've updated this PR so that it's not marked as sensitive again.

@dylanmtaylor
Copy link
Copy Markdown
Contributor Author

@bluca: One thought, we should probably merge #40978 targeting 260 if we're going to do this, so that the existing fields are handled and then I can rebase this onto that.

@bluca
Copy link
Copy Markdown
Member

bluca commented Mar 9, 2026

@bluca I've updated this PR so that it's not marked as sensitive again.

That's great, thanks, but the earlier feedback was that explicit memory clearing and so on is too much for these, which is true. So please look into adding a new flag that doesn't do any of that, but just scrubs the field from dumps/logs/etc. That way everyone should be happy

@keszybz
Copy link
Copy Markdown
Member

keszybz commented Mar 9, 2026

Hmm, do we really care? The additional clearing of memory if a milisecond or two of CPU time. I don't think we should get into the business of distinguishing whether a field is sensitive enough to wipe it or not… Why not just do the easy thing and always wipe it?

@dylanmtaylor
Copy link
Copy Markdown
Contributor Author

@bluca I've updated this PR so that it's not marked as sensitive again.

That's great, thanks, but the earlier feedback was that explicit memory clearing and so on is too much for these, which is true. So please look into adding a new flag that doesn't do any of that, but just scrubs the field from dumps/logs/etc. That way everyone should be happy

@bluca I've updated this PR so that it's not marked as sensitive again.

That's great, thanks, but the earlier feedback was that explicit memory clearing and so on is too much for these, which is true. So please look into adding a new flag that doesn't do any of that, but just scrubs the field from dumps/logs/etc. That way everyone should be happy

Hmm, do we really care? The additional clearing of memory if a milisecond or two of CPU time. I don't think we should get into the business of distinguishing whether a field is sensitive enough to wipe it or not… Why not just do the easy thing and always wipe it?

Hmm, do we really care? The additional clearing of memory if a milisecond or two of CPU time. I don't think we should get into the business of distinguishing whether a field is sensitive enough to wipe it or not… Why not just do the easy thing and always wipe it?

Adding a whole new flag to avoid a few extra CPU cycles seems silly. This is completely negligible performance-wise.

@bluca
Copy link
Copy Markdown
Member

bluca commented Mar 9, 2026

Hmm, do we really care? The additional clearing of memory if a milisecond or two of CPU time. I don't think we should get into the business of distinguishing whether a field is sensitive enough to wipe it or not… Why not just do the easy thing and always wipe it?

Personally I'm fine with it and have no issue one way or the other, and am ok with either option.

But Lennart's feedback was that he didn't want these to be the same as password, with memory wiping and so on, hence why I proposed to add a new flag that goes halfway, as a compromise to try and make everyone happy: #40954 (comment)

@dylanmtaylor
Copy link
Copy Markdown
Contributor Author

dylanmtaylor commented Mar 9, 2026

Hmm, do we really care? The additional clearing of memory if a milisecond or two of CPU time. I don't think we should get into the business of distinguishing whether a field is sensitive enough to wipe it or not… Why not just do the easy thing and always wipe it?

Personally I'm fine with it and have no issue one way or the other, and am ok with either option.

But Lennart's feedback was that he didn't want these to be the same as password, with memory wiping and so on, hence why I proposed to add a new flag that goes halfway, as a compromise to try and make everyone happy: #40954 (comment)

What I don't like about that is that would introduce complexity and it touch the code in more places. We'd probably have to add conditionals on whether or not to clear memory, which is a very cheap operation for something like a date of birth or a short string.

I think adding a separate version of sd_json_variant_sensitive would be a mistake.

@keszybz
Copy link
Copy Markdown
Member

keszybz commented Mar 9, 2026

Yeah. The runtime complexity is negligible. I'd like to avoid the cognitive overhead of deciding between "this field is sensitive" and "this field is even more sensitive, needs to be wiped".

@jackpot51
Copy link
Copy Markdown
Contributor

I'm Jeremy from System76. We are in talks with legislators and there are likely to be amendments to the age verification bills, as well as conflicting requirements in different jurisdictions. It may even be the case that open source operating systems are exempted entirely. I detailed this on the xdg mailing list here:

https://lists.freedesktop.org/archives/xdg/2026-March/014797.html

I have other concerns about this specific implementation. By relying on systemd, which is decidedly unportable to non-Linux operating systems, and not used across all Linux operating systems either, it will force at least one alternative implementation to exist. If these implementations end up having to collect jurisdiction specific requirements, that makes it much harder for compliance.

@keszybz
Copy link
Copy Markdown
Member

keszybz commented Mar 10, 2026

We are in talks with legislators and there are likely to be amendments to the age verification bills, as well as conflicting requirements in different jurisdictions. It may even be the case that open source operating systems are exempted entirely.

It is possible that California law will be changed. But similar ideas are popping up in other contexts and it's unlikely that they'll all go away. This implementation is fairly generic and useful for other things besides age verification, so we shouldn't decide whether to merge it or not based on a single law in any jurisdiction.

By relying on systemd, which is decidedly unportable to non-Linux operating systems, and not used across all Linux operating systems either, it will force at least one alternative implementation to exist.

The API is generic, so somebody can try to reimplement it later if they want to avoid this implementation. But for 99% of systems out there, putting it in systemd is very reasonable.

@jackpot51
Copy link
Copy Markdown
Contributor

We are in talks with legislators and there are likely to be amendments to the age verification bills, as well as conflicting requirements in different jurisdictions. It may even be the case that open source operating systems are exempted entirely.

It is possible that California law will be changed. But similar ideas are popping up in other contexts and it's unlikely that they'll all go away. This implementation is fairly generic and useful for other things besides age verification, so we shouldn't decide whether to merge it or not based on a single law in any jurisdiction.

The California and Colorado law do not require storing a date of birth, which is indirectly identifying PII in the US. An age could instead be stored. Yes, this means that the age bracket could be under-estimated, and may then need to be manually updated to unlock things, but it would significantly reduce the ability to determine an exact person from data stored on the filesystem. The point I want to illustrate is that an operating system using systemd and provided in one of these jurisdictions may want to store an alternative form of data to comply, with a minimum potentially being the age bracket itself. The piece of data stored may end up being jurisdiction dependent, with some jurisdictions explicitly forbidding the storage of an exact birthday while others explicitly require it.

By relying on systemd, which is decidedly unportable to non-Linux operating systems, and not used across all Linux operating systems either, it will force at least one alternative implementation to exist.

The API is generic, so somebody can try to reimplement it later if they want to avoid this implementation. But for 99% of systems out there, putting it in systemd is very reasonable.

I don't disagree that the userdb varlink API could be implemented on other open-source operating systems, and that systemd covers a wide set of them.

@bluca
Copy link
Copy Markdown
Member

bluca commented Mar 10, 2026

The California and Colorado law do not require storing a date of birth, which is indirectly identifying PII in the US.

So are the name and location. This is a user account mechanism, so it stores various information, including PII, as it's obvious it needs to. What upper layers do with that information, and whether to store it in the first place, is up to them, and is independent of specific requirements and whatnot. This implementation can be used just fine to implement any variety of requirements, or none at all, so it's fine as it is.

@systemd systemd locked as too heated and limited conversation to collaborators Mar 10, 2026
@bluca
Copy link
Copy Markdown
Member

bluca commented Mar 10, 2026

This clearly got linked somewhere, so let's lock it for a while to avoid spam

@poettering
Copy link
Copy Markdown
Member

i still find the marking as sensitive silly, but i also don't care enough.

lgtm

@keszybz
Copy link
Copy Markdown
Member

keszybz commented Mar 10, 2026

@dylanmtaylor in the future, no need to request re-review… People who commented are notified automatically.

@AdrianVovk
Copy link
Copy Markdown
Contributor

Follow-up work (not in this PR): The accountsservice PR changed to require polkit authentication before showing the user's birthdate, even if you're the user yourself. Essentially, this constitutes a section in userdb similar to protected, but only administrators can see it - the user themself can't.

In userdb's case, I think it makes sense to keep birthDate in the regular section, but also allow it in the protected section. This is different from AccountsService, which removed the equivalent of birthDate in the regular section. This difference in behavior is appropriate because userdb is so much more flexible than accountsservice...

@poettering
Copy link
Copy Markdown
Member

BTW, just to emphasize this, and explain why i think adding this to userdb is fine:

i actually believe that making sure apps cannot just read the birthday field is a good idea, but it's just one tiny piece of data among so so much more important stuff. if people run apps unsandboxed these apps get access to any file in $HOME and a tonload more stuff of the system. And that data is a lot more valuable than the birthday is. Hence, let's maybe not waste discussion around isolating apps from that single piece of information that is the birthday, while leaving everything else wide open.

The answer to the PII issues is hence not restrictions in userdb, the answer is proper app sandboxing. And that even already exists in flatpak! It restricts access to $HOME already, and to userdb too! And that's the way to do it!

Hence, just embrace app sandboxing! And if you come to me and say "hey, I run all my apps without sandboxing, but i want the birthday hidden anyway" then I can only say, your model is really really broken. Fix your security model first, then come back.

So, I think userdb should reveal the birthday to per-user code, because that per-user code then can consume this and provide a portal or something to properly sandboxed apps that provides a more restrictive api, i.e. age brackets and so on. But that kind of stuff is outside of the scope of systemd, here in systemd we just provide you with a way to maintain the original data, the precise policy enforcement on it must happen in the sandbox.

@poettering
Copy link
Copy Markdown
Member

@claude review

Move the YYYY-MM-DD date parsing and validation logic from
sysupdate-resource.c into a shared parse_calendar_date() function
in time-util, so it can be reused by other subsystems.
Add a birthDate field to the JSON user record, stored internally as a
struct tm with INT_MIN/negative sentinels for unset fields. The field
is serialized as a YYYY-MM-DD string in JSON and validated via
parse_birth_date(), which shares its core logic with
parse_calendar_date() through a new parse_calendar_date_full()
function.

For birth dates, timegm() is called directly (rather than
mktime_or_timegm_usec) to support pre-epoch dates. The wday field is
used to distinguish timegm() failure from a valid (time_t) -1 return.

birthDate is excluded from user_record_self_modifiable_fields(), so
only administrators can set or change it via homectl. The field
remains in the regular (non-privileged) JSON section, keeping it
readable by the user and applications.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Development

Successfully merging this pull request may close these issues.