Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

There are many guides to the steps for correctly launching a daemon process, such as Stevens' chapter in Advanced Programming in the UNIX Environment. There is less agreement on what steps should be taken after daemonizing, going the other way: launching a user process from a root process.

Historically, the details of calls in login or telnetd have differed widely by platform. An SSH server is a modern example of a login daemon. But, there are different interpretations of the correct steps in different implementations of SSH servers, which usually heavily dependent on per-platform steps.

How should one write a modern daemon that provides logins, setting up the privileges and context to run a user's commands, perhaps based on remote input?

share|improve this question
    
Is there a one-stop guide to all these already, or do we have to write one here? I have a good set of notes here already to kick off a wiki question if so, so don't get too busy answering straight away! –  Nicholas Wilson Jan 20 '13 at 23:04
    
Historically, there is no login daemon. There is a getty process, that eventually spawns a login process unless you're somewhat snotty. –  ott-- Jan 22 '13 at 22:10
    
Well, telnetd is pretty historical, and some quite historic implementations do a lot of the legwork that's also in login(1). I'm trying to assemble here a list of all that. Some OpenBSM and Mach "ports" notes coming up... Nearly forgot them! –  Nicholas Wilson Jan 22 '13 at 23:50

1 Answer 1

up vote 2 down vote accepted

Introduction

The basic task of a daemon providing logins is to execute one or more commands in the correct context for a user, for that system. This is harder than it seems, given historical requirements derived from terminal logins, and differences in process attributes and credentials between platforms. Different steps must be precisely ordered to ensure correct set-up.

Practical example

The github project netlogind is a simple test application that demonstrates the operation of such a daemon. It illustrates the steps listed below.

Basic steps to create a process running as a given user

struct passwd pw; //< the user
setgid(pw.pw_gid);
initgroups(pw.pw_name, pw.pw_gid);
setuid(pw.pw_uid);

(Error checking should be done.) In addition, for highly security-critical calls such as setuid, call getuid and geteuid afterwards to assert that the correct credentials were set. Continuing to execute code under the wrong uid is the worst disaster of all. setuid resets the saved-set-userid on all platforms where this is supported.

PAM

PAM is an API allowing system administrators to configure how applications perform authentication and launch user processes. PAM is widely deployed.

PAM is used to set up session environment through the pam_setcred and pam_open_session functions. There are many issues with calling these functions portably, and constraints on the order.

  • They must be called from the same thread of execution as pam_authenticate if that was used to perform authentication. Some modules work by collecting credentials during the authentication conversation, and performing an action with them during the session phase (eg pam_mount). In particular, PAM modules that use pam_set_data internally will not work if pam_setcred/open_session is called from a different process to pam_authenticate (for example, some versions of pam_afs or `pam_krb5).
  • They must be called as root.
  • They must be called after initgroups, as they may be used to set up extra group memberships.
  • There is debate over which order pam_setcred and pam_open_session should be called in. It seems preferable to invoke pam_setcred before pam_open_session on most modern platforms, as some modules reasonably require this.[*] However, there are reasons for wanting to order it the other way.[*] Regardless, the strictest constraint is that Solaris and HP-UX PAM will fail with certain modules unless pam_setcred comes second, so there is no much choice on those platforms (that is, you actually have to follow the order documented on those platforms). LinuxPAM's documentation says that `pam_setcred should come first, the opposite to OpenPAM's documentation.
  • Ordering of pam_open_session/setcred relative to forking: Do not fork between calling the two PAM functions and setuid. pam_limits applies resource limits to the calling process, and root could very well be running more processes than the target user is allowed to run, in which case forking will always fail.
  • PAM bugs to be aware of: some vendor-supplied modules, eg on HP-UX, do not pass the appdata parameter to the conversation function. For portability, use a static variable instead to avoid relying on the appdata parameter. Other notable real-world compatibility issues: RedHat #126985, RedHat #127054, the PAM_TTY issues on Sun (eg OpenSSH #687), ruid restrictions on pam_chauthtok (AIX requires ruid of 0, Solaris requires ruid non-zero).

Setting up the execution environment for a user process

closefrom

Close all fds before exec'ing the user's command. Whether this should be done is debated, because it kills many implementations of posix_trace (for example). Although sometimes listed as one of the steps for daemonizing a process, it's a very paranoid thing to do. It's more justifiable to do though when creating a user session.

Platforms: native on Solaris, FreeBSD. Otherwise, emulate using fds listed in proc if available. On no account naively try to close up to getrlimit(RLIMIT_NOFILE) or similar, as this can be far too large a number to loop up to.

Call at: any time

See also: Austin Group Defect Tracker, "Add fdwalk system interface"

setlogin

Invoke setlogin(pw.pw_name) to ensure that the session has the correct name associated with it.

Call: Right after a setsid; absolutely not from the same session the daemon is running in. Call as root.

Platforms: FreeBSD, Mac OSX. Because one uid may have several entries in the password database with different names, getpwuid(getuid()) mightn't tell you the username that was used to log on, so another function, getlogin, has to be provided to do this. The implementation may be done in terms of utmp (unreliable), or $LOGNAME (insecure). BSD-derived systems solve the problem in the ideal way by storing a username in the per-session kernel data structure. AIX solves this using usrinfo (below)

usrinfo, setpcred

On AIX, call usrinfo(SETUINFO, "LOGIN=<name>\0LOGNAME=<name>\0NAME=name\0", ...). This is similar in function to setlogin on BSD-derived systems. Call as root.

Use setpcred(pw.pw_name, NULL) to set up process limits correctly from the credentials in the user database.

Environment variables

$USER, $HOME, $PATH, $LOGNAME, $LOGIN (legacy, AIX)

Optional: $MAIL, $TZ

Defaults may be in /etc/environment.

More detail on env vars needed! Edit me!

SELinux

Setting the execution context of the child process is best done through PAM on Linux systems. It could however be set manually, to guarantee that any privileges of the daemon are not applied to the user process, irrespective of PAM configuration. In this case, it would set before calling initgroups.

Audit userid

On some Linux kernels, processes maintain an auid, an additional userid which is preserved when the user switches userid using su(1), for example. This permits actions taken to be logged and traced to the user who performed it.

The auid is typically set using pam_loginid. To guarantee it is set even when PAM is not configured correctly, write the user's uid to /proc/self/loginuid.

See further: "The Linux Audit System, or Who Changed That File?", Rainer Wichmann

Solaris contract(4)

Create a new contract for processes launched from a daemon.

See example: "Creating subprocesses in new contracts on Solaris 10", Floris Bruynooghe

setusercontext

Many of these tasks are factored out of login(1) into libutil on BSD systems. See setusercontext documentation.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.