old-www/HOWTO/Shadow-Password-HOWTO-8.html

337 lines
12 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<HTML>
<HEAD>
<META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
<TITLE>Linux Shadow Password HOWTO: Adding shadow support to a C program</TITLE>
<LINK HREF="Shadow-Password-HOWTO-9.html" REL=next>
<LINK HREF="Shadow-Password-HOWTO-7.html" REL=previous>
<LINK HREF="Shadow-Password-HOWTO.html#toc8" REL=contents>
</HEAD>
<BODY>
<A HREF="Shadow-Password-HOWTO-9.html">Next</A>
<A HREF="Shadow-Password-HOWTO-7.html">Previous</A>
<A HREF="Shadow-Password-HOWTO.html#toc8">Contents</A>
<HR>
<H2><A NAME="sec-adding"></A> <A NAME="s8">8. Adding shadow support to a C program</A></H2>
<P>Adding shadow support to a program is actually fairly straightforward. The
only problem is that the program must be run by root (or SUID root) in order
for the the program to be able to access the <CODE>/etc/shadow</CODE> file.
<P>This presents one big problem: very careful programming practices must be
followed when creating SUID programs. For instance, if a program has a shell
escape, this must not occur as root if the program is SUID root.
<P>For adding shadow support to a program so that it can check passwords, but
otherwise does need to run as root, it's a lot safer to run the program SUID
shadow instead. The <CODE>xlock</CODE> program is an example of this.
<P>In the example given below, <CODE>pppd-1.2.1d</CODE> already runs SUID as root,
so adding shadow support should not make the program any more vulnerable.
<P>
<H2><A NAME="ss8.1">8.1 Header files</A>
</H2>
<P>The header files should reside in <CODE>/usr/include/shadow</CODE>. There
should also be a <CODE>/usr/include/shadow.h</CODE>, but it will be a symbolic
link to <CODE>/usr/include/shadow/shadow.h</CODE>.
<P>To add shadow support to a program, you need to include the header files:
<PRE>
#include &lt;shadow/shadow.h>
#include &lt;shadow/pwauth.h>
</PRE>
<P>It might be a good idea to use compiler directives to conditionally compile
the shadow code (I do in the example below).
<P>
<H2><A NAME="ss8.2">8.2 libshadow.a library</A>
</H2>
<P>When you installed the <EM>Shadow Suite</EM> the <CODE>libshadow.a</CODE> file
was created and installed in <CODE>/usr/lib</CODE>.
<P>When compiling shadow support into a program, the linker needs to be told to
include the <CODE>libshadow.a</CODE> library into the link.
<P>This is done by:
<BLOCKQUOTE><CODE>
<PRE>
gcc program.c -o program -lshadow
</PRE>
</CODE></BLOCKQUOTE>
<P>However, as we will see in the example below, most large programs use a
<CODE>Makefile</CODE>, and usually have a variable called <CODE>LIBS=...</CODE> that
we will modify.
<P>
<H2><A NAME="ss8.3">8.3 Shadow Structure</A>
</H2>
<P>The <CODE>libshadow.a</CODE> library uses a structure called <CODE>spwd</CODE>
for the information it retrieves from the <CODE>/etc/shadow</CODE> file. This is
the definition of the <CODE>spwd</CODE> structure from the
<CODE>/usr/include/shadow/shadow.h</CODE> header file:
<HR>
<PRE>
struct spwd
{
char *sp_namp; /* login name */
char *sp_pwdp; /* encrypted password */
sptime sp_lstchg; /* date of last change */
sptime sp_min; /* minimum number of days between changes */
sptime sp_max; /* maximum number of days between changes */
sptime sp_warn; /* number of days of warning before password
expires */
sptime sp_inact; /* number of days after password expires
until the account becomes unusable. */
sptime sp_expire; /* days since 1/1/70 until account expires
*/
unsigned long sp_flag; /* reserved for future use */
};
</PRE>
<HR>
<P>The <EM>Shadow Suite</EM> can put things into the <CODE>sp_pwdp</CODE> field
besides just the encoded passwd. The password field could contain:
<BLOCKQUOTE><CODE>
<PRE>
username:Npge08pfz4wuk;@/sbin/extra:9479:0:10000::::
</PRE>
</CODE></BLOCKQUOTE>
<P>This means that in addition to the password, the program
<CODE>/sbin/extra</CODE> should be called for further authentication. The
program called will get passed the username and a switch that indicates
why it's being called. See the file <CODE>/usr/include/shadow/pwauth.h</CODE>
and the source code for <CODE>pwauth.c</CODE> for more information.
<P>What this means is that we should use the function <CODE>pwauth</CODE> to
perform the actual authentication, as it will take care of the secondary
authentication as well. The example below does this.
<P>The author of the <EM>Shadow Suite</EM> indicates that since most
programs in existence don't do this, and that it may be removed or changed
in future versions of the <EM>Shadow Suite</EM>.
<P>
<H2><A NAME="ss8.4">8.4 Shadow Functions</A>
</H2>
<P>The <CODE>shadow.h</CODE> file also contains the function prototypes for the
functions contained in the <CODE>libshadow.a</CODE> library:
<HR>
<PRE>
extern void setspent __P ((void));
extern void endspent __P ((void));
extern struct spwd *sgetspent __P ((__const char *__string));
extern struct spwd *fgetspent __P ((FILE *__fp));
extern struct spwd *getspent __P ((void));
extern struct spwd *getspnam __P ((__const char *__name));
extern int putspent __P ((__const struct spwd *__sp, FILE *__fp));
</PRE>
<HR>
<P>The function that we are going to use in the example is: <CODE>getspnam</CODE>
which will retrieve for us a <CODE>spwd</CODE> structure for the supplied name.
<P>
<P>
<H2><A NAME="ss8.5">8.5 Example</A>
</H2>
<P>This is an example of adding shadow support to a program that needs it, but
does not have it by default.
<P>This example uses the <EM>Point-to-Point Protocol Server</EM> (pppd-1.2.1d),
which has a mode in which it performs <EM>PAP</EM> authentication using
user names and passwords from the <CODE>/etc/passwd</CODE> file instead of the
<EM>PAP</EM> or <EM>CHAP</EM> files. You would not need to add this code
to <CODE>pppd-2.2.0</CODE> because it's already there.
<P>This feature of pppd probably isn't used very much, but if you installed the
<EM>Shadow Suite</EM>, it won't work anymore because the passwords are no
longer stored in <CODE>/etc/passwd</CODE>.
<P>The code for authenticating users under <CODE>pppd-1.2.1d</CODE> is located in
the <CODE>/usr/src/pppd-1.2.1d/pppd/auth.c</CODE> file.
<P>The following code needs to be added to the top of the file where all the
other <CODE>#include</CODE> directives are. We have surrounded the
<CODE>#includes</CODE> with conditional directives (i.e. only include if we
are compiling for shadow support).
<P>
<HR>
<PRE>
#ifdef HAS_SHADOW
#include &lt;shadow.h>
#include &lt;shadow/pwauth.h>
#endif
</PRE>
<HR>
<P>The next thing to do is to modify the actual code. We are still making
changes to the <CODE>auth.c</CODE> file.
<P>Function <CODE>auth.c</CODE> before modifications:
<HR>
<PRE>
/*
* login - Check the user name and password against the system
* password database, and login the user if OK.
*
* returns:
* UPAP_AUTHNAK: Login failed.
* UPAP_AUTHACK: Login succeeded.
* In either case, msg points to an appropriate message.
*/
static int
login(user, passwd, msg, msglen)
char *user;
char *passwd;
char **msg;
int *msglen;
{
struct passwd *pw;
char *epasswd;
char *tty;
if ((pw = getpwnam(user)) == NULL) {
return (UPAP_AUTHNAK);
}
/*
* XXX If no passwd, let them login without one.
*/
if (pw->pw_passwd == '\0') {
return (UPAP_AUTHACK);
}
epasswd = crypt(passwd, pw->pw_passwd);
if (strcmp(epasswd, pw->pw_passwd)) {
return (UPAP_AUTHNAK);
}
syslog(LOG_INFO, "user %s logged in", user);
/*
* Write a wtmp entry for this user.
*/
tty = strrchr(devname, '/');
if (tty == NULL)
tty = devname;
else
tty++;
logwtmp(tty, user, ""); /* Add wtmp login entry */
logged_in = TRUE;
return (UPAP_AUTHACK);
}
</PRE>
<HR>
<P>The user's password is placed into <CODE>pw->pw_passwd</CODE>, so all we really
need to do is add the function <CODE>getspnam</CODE>. This will put the
password into <CODE>spwd->sp_pwdp</CODE>.
<P>We will add the function <CODE>pwauth</CODE> to perform the actual authentication.
This will automatically perform secondary authentication if the shadow file
is setup for it.
<P>Function <CODE>auth.c</CODE> after modifications to support shadow:
<P>
<HR>
<PRE>
/*
* login - Check the user name and password against the system
* password database, and login the user if OK.
*
* This function has been modified to support the Linux Shadow Password
* Suite if USE_SHADOW is defined.
*
* returns:
* UPAP_AUTHNAK: Login failed.
* UPAP_AUTHACK: Login succeeded.
* In either case, msg points to an appropriate message.
*/
static int
login(user, passwd, msg, msglen)
char *user;
char *passwd;
char **msg;
int *msglen;
{
struct passwd *pw;
char *epasswd;
char *tty;
#ifdef USE_SHADOW
struct spwd *spwd;
struct spwd *getspnam();
#endif
if ((pw = getpwnam(user)) == NULL) {
return (UPAP_AUTHNAK);
}
#ifdef USE_SHADOW
spwd = getspnam(user);
if (spwd)
pw->pw_passwd = spwd->sp-pwdp;
#endif
/*
* XXX If no passwd, let NOT them login without one.
*/
if (pw->pw_passwd == '\0') {
return (UPAP_AUTHNAK);
}
#ifdef HAS_SHADOW
if ((pw->pw_passwd &amp;&amp; pw->pw_passwd[0] == '@'
&amp;&amp; pw_auth (pw->pw_passwd+1, pw->pw_name, PW_LOGIN, NULL))
|| !valid (passwd, pw)) {
return (UPAP_AUTHNAK);
}
#else
epasswd = crypt(passwd, pw->pw_passwd);
if (strcmp(epasswd, pw->pw_passwd)) {
return (UPAP_AUTHNAK);
}
#endif
syslog(LOG_INFO, "user %s logged in", user);
/*
* Write a wtmp entry for this user.
*/
tty = strrchr(devname, '/');
if (tty == NULL)
tty = devname;
else
tty++;
logwtmp(tty, user, ""); /* Add wtmp login entry */
logged_in = TRUE;
return (UPAP_AUTHACK);
}
</PRE>
<HR>
<P>Careful examination will reveal that we made another change as well. The
original version allowed access (returned <CODE>UPAP_AUTHACK</CODE> if there
was NO password in the <CODE>/etc/passwd</CODE> file. This is <EM>not</EM>
good, because a common use of this login feature is to use one account
to allow access to the PPP process and then check the username and password
supplied by PAP with the username in the <CODE>/etc/passwd</CODE> file and
the password in the <CODE>/etc/shadow</CODE> file.
<P>So if we had set the original version up to run as the shell for a user i.e.
<CODE>ppp</CODE>, then anyone could get a ppp connection by setting their PAP to user
<CODE>ppp</CODE> and a password of null.
<P>We fixed this also by returning <CODE>UPAP_AUTHNAK</CODE> instead of
<CODE>UPAP_AUTHACK</CODE> if the password field was empty.
<P>Interestingly enough, <CODE>pppd-2.2.0</CODE> has the same problem.
<P>Next we need to modify the Makefile so that two things occur:
<CODE>USE_SHADOW</CODE> must be defined, and <CODE>libshadow.a</CODE> needs to be
added to the linking process.
<P>Edit the Makefile, and add:
<BLOCKQUOTE><CODE>
<PRE>
LIBS = -lshadow
</PRE>
</CODE></BLOCKQUOTE>
<P>Then we find the line:
<BLOCKQUOTE><CODE>
<PRE>
COMPILE_FLAGS = -I.. -D_linux_=1 -DGIDSET_TYPE=gid_t
</PRE>
</CODE></BLOCKQUOTE>
<P>And change it to:
<BLOCKQUOTE><CODE>
<PRE>
COMPILE_FLAGS = -I.. -D_linux_=1 -DGIDSET_TYPE=gid_t -DUSE_SHADOW
</PRE>
</CODE></BLOCKQUOTE>
<P>Now make and install.
<P>
<HR>
<A HREF="Shadow-Password-HOWTO-9.html">Next</A>
<A HREF="Shadow-Password-HOWTO-7.html">Previous</A>
<A HREF="Shadow-Password-HOWTO.html#toc8">Contents</A>
</BODY>
</HTML>