Hi Marc,

I have a working implementation of the counter, with tests, and running it 
raised four questions I would rather settle with you than guess.

1. A single counter does not survive sub-ranges

The naive reading of "store the last uid and increment from there" breaks when 
a range is requested explicitly:

  # REUSE_IDS=no
  adduser --firstuid 5000 --lastuid 5010 rhigh     # gets 5000, counter=5000
  addgroup --firstgid 2090 --lastgid 2093 rlow
  warn: No GID is available in the range 5001-2093 (FIRST_GID - LAST_GID)
  fatal: The group `rlow' was not created.

The 2090-2093 range is completely empty, but the counter of the default range 
pushed the floor above its ceiling.

My fix is to make the range part of the state file key, so each range carries 
its own counter:

  @global:last_id_1000_59999=1006:last_sys_id_100_999=104

The guarantee becomes "an id is not handed out twice within the range it came 
from". Does that match what you had in mind, or would you rather have a single 
counter with a different rule for sub-ranges?

2. Should an explicit range turn off the automatism?

You wrote that for --uid "the wish of the local admin wins and turns off the 
automatism". Asking for --firstuid/--lastuid looks like the same kind of 
explicit wish to me, and treating it that way would also leave 
firstlastuidgid.t untouched. But it does mean that an admin who always carves 
ranges never gets the guarantee. Your call.

3. The default is more expensive than it looks

With the counter always on, 122 assertions fail in firstlastuidgid.t, 
firstlastuidgid_orig.t, uidgidpool.t and suidsgidpool.t. They are not stale: 
they encode the current contract, that adduser picks the first free id of the 
range. After any deletion that stops being true.

So I currently default REUSE_IDS to yes, which leaves every existing 
installation and the whole test suite untouched, and lets an admin opt in. 
Flipping it to no is a one-word change plus a NEWS.Debian entry, but it needs 
those tests adapted. Which do you prefer?

I did not want to rewrite assertions in firstlastuidgid*.t anyway, since you 
are reworking that file in wip/new-firstlastuidgid.

4. Where should the counter live inside the state file?

The store is keyed by user name and a counter belongs to no user. Rather than 
storing it under a fake user name, I taught _read_state() and _write_state() 
about a reserved '@global' record, so delete_state_user() can not take the 
counters with it. That touches the format of your module, so tell me if you 
prefer something else.

5. Is a second knob wanted at all?

You never mentioned configuration, so this may be over-engineering on my side: 
I added ID_COUNTER_INIT=first_free|highest, because starting at the first free 
id (as you suggested) cannot cover accounts that were deleted before the state 
file existed, which was Aaron Hall's unanswered question from 2006. If you 
would rather not have that option, I will drop it.

Unrelated to this bug, I also came across a couple of small things while 
reading the code (a duplicated exit code value that contradicts the manpage, 
among others). I will file those separately rather than mixing them in here.

-- 
     [o]---[o]---[o]
      | \   |   / |      Juan Muñoz <[email protected]>
     [o]---[*]---[o]     Building things the universe didn't ask for.
      | /   |   \ |      Tor relay operator · RIPE Atlas probe
     [o]---[o]---[o]     OONI Probe · salsa.debian.org/juaesm

Reply via email to