/*
* Copyright (c) 1985 Corporation for Research and Educational Networking
* Copyright (c) 1988 University of Illinois Board of Trustees, Steven
* Dorner, and Paul Pomes
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the Corporation for
* Research and Educational Networking (CREN), the University of
* Illinois at Urbana, and their contributors.
* 4. Neither the name of CREN, the University nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE TRUSTEES AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
** Login and Logout functions, using the many flavors of password
** protocols (original recipe, Kerberos, email etc.)
**
**
** LoginQi - Login to QI server, optionally prompting for username/password.
**
** Parameters:
** UseHost - name of Qi server host
** ToQI - stream descriptor to write to
** FromQI - stream descriptor to read from
** Options - see qiapi.h/LQ_* defines
** Username - pointer to name to login as (alias) or NULL
** Password - pointer to password or NULL
**
** Returns:
** alias logged in as or NULL.
**
** Side Effects:
** possibly obtains and caches Kerberos tickets.
** username/password prompts are written/read to/from stdin/out,
** iff Options&LQ_INTERACTIVE.
**
** (most of this code lifted out of ph 6.5)
*/
/*
* try each kind of login protocol in turn 'til one succeeds or we run
* out of choices. To avoid reprompting for Username or Password, it
* is the responsiblity of each routine to malloc up the result of
* obtaining the username/password, and the responsibility of this
* routine to clean them up -- unless they were passed in from caller of
* course.
*/
char *
LoginQi(UseHost, ToQI, FromQI, Options, Username, Password)
const char *UseHost;
FILE *ToQI, *FromQI;
int Options;
const char *Username, *Password;
{
char *U = (char *)Username, *P = (char *)Password;
static char MyAlias[MAXSTR];
memset(MyAlias,0,sizeof MyAlias);
#ifdef EMAIL_AUTH
if (Options&LQ_EMAIL &&
LoginEmail(UseHost, ToQI, FromQI, Options, MyAlias, &U, &P) == LR_OK)
goto LoggedIn;
#endif /* EMAIL_AUTH */
#ifdef KERBEROS
if (Options&LQ_KERBEROS &&
LoginKRB(UseHost, ToQI, FromQI, Options, MyAlias, &U, &P) == LR_OK)
goto LoggedIn;
#endif /* KERBEROS */
if (Options&LQ_LOGIN)
(void) LoginOriginal(UseHost, ToQI, FromQI, Options, MyAlias, &U, &P);
LoggedIn:
if (!Username && U) /* username was not passed in */
free(U); /* so free malloc'd string */
if (!Password && P) { /* ditto for password */
memset(P,0,strlen(P));
free(P);
}
fputs(MsgBuf, stdout);
return (*MyAlias)? MyAlias : NULL;
}
/*
* Original recipe login, based on shared secret (password) between QI
* server and user. If autologin is selected, .netrc is tried first.
* If LoginQiEmailAuth is true, then email auth is attempted.
*/
static int LoginQiEmailAuth = 0; /* a dirty little secret */
/* if LQ_AUTO option selection and a username is not supplied,
try getting the login info from .netrc */
if (Options&LQ_AUTO && !*Up) { /* try autologin w/.netrc */
GetAutoLogin(Up,Pp);
if (QiDebug)
fprintf(stderr,"autologin: .netrc user=%s, pass=%s\n",
(*Up)?*Up:"(none)", (*Pp)?*Pp:"(none)");
}
if (!*Up) { /* username not supplied */
if (!(Options&LQ_INTERACTIVE)) /* sorry, I can't ask you. */
return (LR_ERROR);
printf(NAMEPROMPT); /* ask for missing alias */
fgets(scratch, sizeof (scratch), stdin);
scratch[strlen(scratch)] = '\0'; /* zap the \n */
if (!*scratch)
return (LR_ERROR);
*Up = strdup(scratch);
}
if (!*Pp &&!LoginQiEmailAuth && !(Options&LQ_INTERACTIVE))
return (LR_ERROR); /* I can't ask your password */
if (QiDebug)
fprintf(stderr, "sent=login %s\n", *Up); /*send login request */
fprintf(ToQI, "login %s\n", *Up); /*send login request */
fflush(ToQI);
for (;;) /*read the response */
{
if (!GetGood(MsgBuf, MAXSTR, FromQI))
{
fprintf(stderr, "Whoops--the nameserver died.\n");
return LR_ERROR;
}
code = atoi(MsgBuf);
if (code != LR_LOGIN) /*intermediate or strange response */
fputs(MsgBuf, stdout);
if (code >= LR_OK) /*final response */
break;
}
if (code == LR_LOGIN)
{
if (LoginQiEmailAuth) { /* try email login */
char *me = getpwuid(getuid())->pw_name;
if (QiDebug)
fprintf(stderr, "sent=email %s\n",me);
fprintf(ToQI, "email %s\n", me);
} else {
if (!*Pp) { /* password not supplied */
char *newp;
newp = getpass(PASSPROMPT);
*Pp = strdup(newp);
}
if (strlen(*Pp) > PH_PW_LEN) {
char *cp = &(*Pp)[PH_PW_LEN];
while (*cp)
*cp++ = '\0'; /* null out *all* the extras */
}
crypt_start(*Pp);
/*encrypt the challenge with the password */
MsgBuf[strlen(MsgBuf) - 1] = '\0'; /*strip linefeed */
scratch[encryptit(scratch, (char *)strchr(MsgBuf, ':') + 1)] = '\0';
/*send the encrypted text to qi */
if (QiDebug)
fprintf(stderr, "sent=answer %s\n", scratch);
fprintf(ToQI, "answer %s\n", scratch);
}
}
fflush(ToQI);
/*get the final response */
for (;;)
{
if (!GetGood(MsgBuf, MAXSTR, FromQI))
{
fprintf(stderr, "Whoops--the nameserver died.\n");
return LR_ERROR;
}
code = atoi(MsgBuf);
if (code >= LR_OK) /*final response */
break;
}
if (code == LR_OK) /*logged in */
{
strcpy(MyAlias, (char *)strchr(MsgBuf, ':') + 1);
*(char *)strchr(MyAlias, ':') = '\0';
} else
*MyAlias = '\0';
return (code);
}
/*
* check .netrc to for username and password to try to login with.
*/
static void
GetAutoLogin(alias,pw)
char **alias, **pw; /* filled in from .netrc */
{
FILE *netrc; /*the .netrc file */
char path[1024]; /*pathname of .netrc file */
struct stat statbuf; /*permissions, etc. of .netrc file */
char key[80], val[80]; /*line from the .netrc file */
char *token; /*token (word) from the line from the .netrc file */
static void SkipMacdef __P((FILE *));
/*
* manufacture the pathname of the user's .netrc file
*/
sprintf(path, "%s/.netrc", getenv("HOME"));
/*
* make sure its permissions are ok
*/
if (stat(path, &statbuf) < 0)
return;
if (statbuf.st_mode & 077)
return; /*refuse insecure files */
/*
* try to open it
*/
if (!(netrc = fopen(path, "r")))
return;
/*
* look for a ``machine'' named ``ph''
*/
while (2 == fscanf(netrc, "%s %s", key, val))
{
if (!strcmp(key, "machine") && !strcmp(val, CLIENT))
{
/*
* found an entry for ph. look now for other items
*/
while (2 == fscanf(netrc, "%s %s", key, val))
{
if (!strcmp(key, "machine")) /*new machine */
goto out;
else if (!strcmp(key, "login"))
*alias = strdup(val);
else if (!strcmp(key, "password"))
*pw = strdup(val);
else if (!strcmp(key, "macdef"))
SkipMacdef(netrc);
}
} else if (!strcmp(key, "macdef"))
SkipMacdef(netrc);
}
out:
return;
}
/*
* skip a macdef in the .netrc file
*/
static void
SkipMacdef(netrc)
FILE *netrc;
{
int c, wasNl;
for (wasNl = 0; (c = getc(netrc)) != EOF; wasNl = (c == '\n'))
if (wasNl && c == '\n')
break;
}
#ifdef KERBEROS
/*
* Extra-Krispy recipe, using a trusted third party with a strange name.
*/
static int
LoginKRB(UseHost, ToQI, FromQI, Options, MyAlias, Up, Pp)
const char *UseHost;
FILE *ToQI, *FromQI;
int Options;
char *MyAlias, **Up, **Pp;
{
struct sockaddr_in sin, lsin;
int status;
int sock = fileno(ToQI);
int namelen;
KTEXT_ST ticket;
long authopts;
MSG_DAT msg_data;
CREDENTIALS cred;
Key_schedule sched;
static char scratch[MAXSTR];
char principal[ANAME_SZ];
char instance[INST_SZ];
char realm[REALM_SZ], *hrealm;
int code;
char krbtkfile[MAXPATHLEN];
char okrbtkfile[MAXPATHLEN];
static char kpass[BUFSIZ];
/* find out who I am */
namelen = sizeof (lsin);
if (getsockname(sock, (struct sockaddr *) & lsin, &namelen) < 0)
{
perror("getsockname");
return (LR_ERROR);
}
/* find out who the other side is */
namelen = sizeof (sin);
if (getpeername(sock, (struct sockaddr *) & sin, &namelen) < 0)
{
perror("getpeername");
return (LR_ERROR);
}
/*
* Did the user specify a username? Has autologin been requested?
* If not, and if we're not logged in to kerberos, prompt for one.
*/
if (!*Up)
{
struct stat dummy;
if (!(Options&LQ_AUTO)) /* no user, no autologin */
return (LR_ERROR); /* no deal */
if (stat(TKT_FILE, &dummy)) /* no ticket cache */
{
if (!(Options&LQ_INTERACTIVE)) /* can't ask */
return (LR_ERROR);
printf(NAMEPROMPT);
fgets(scratch, sizeof (scratch), stdin);
if (!*scratch)
return (LR_ERROR);
else
{
/* zap newline*/
scratch[strlen(scratch) - 1] = 0;
*Up = strdup(scratch);
}
}
}
/* If we're not already logged in with kerberos then do so (get a TGT).
(NULL username at this point implies we already have a TGT). */
if (*Up)
{
*principal = *instance = *realm = '\0';
status = kname_parse(principal, instance, realm, *Up);
if (status != KSUCCESS)
{
fprintf(stderr, "%s\n", krb_err_txt[status]);
return LR_ERROR;
}
if (!*realm && krb_get_lrealm(realm, 1))
{
fprintf(stderr, "Unable to get realm.\n");
return LR_ERROR;
}
/* set tkt file we'll use */
strcpy(okrbtkfile, TKT_FILE);
sprintf(krbtkfile, "/tmp/tkt_ph_%d", getpid());
krb_set_tkt_string(krbtkfile);
if (!*Pp) { /* no password supplied */
if (!(Options&LQ_INTERACTIVE)) /* I can't ask */
return LR_ERROR;
if (des_read_pw_string(kpass,sizeof(kpass),PASSPROMPT,0) != 0) {
fprintf(stderr, "Unable to read password.\n");
return LR_ERROR;
}
*Pp = strdup(kpass);
}
/* login */
status = krb_get_pw_in_tkt(principal, instance, realm,
"krbtgt", realm, 96, *Pp);
if (QiDebug)
fprintf(stderr, "%s getting kerberos ticket granting ticket.\n",
(status == KSUCCESS)?"success":"failure");
if (status != KSUCCESS)
{
if (*Up)
{
krb_set_tkt_string(okrbtkfile);
}
return LR_ERROR;
}
}
if (QiDebug)
fprintf(stderr, "sent=klogin\n"); /* send login request */
fprintf(ToQI, "klogin\n"); /* send login request */
fflush(ToQI);
for (;;) /* read the response */
{
if (!GetGood(MsgBuf, MAXSTR, FromQI))
{
fprintf(stderr, "Whoops--the nameserver died.\n");
if (*Up)
dest_tkt(); /* destroy temp tickets for
specified username */
memset(kpass,0,sizeof(kpass)); /* paranoia */
return(LR_ERROR);
}
code = atoi(MsgBuf);
if (code != LR_LOGIN) /* intermediate or strange response */
fputs(MsgBuf, stdout);
if (code >= LR_OK) /* final response */
break;
}
if (code == LR_LOGIN)
{
/*
* call Kerberos library routine to obtain an authenticator,
* pass it over the socket to the server, and obtain mutual
* authentication.
*/
#ifdef KRBNSREALM
hrealm = KRBNSREALM;
#else
hrealm = krb_realmofhost(UseHost);
#endif
authopts = KOPT_DO_MUTUAL;
status = krb_sendauth(authopts, sock, &ticket,
NSSERVICE, UseHost, hrealm,
0, &msg_data, &cred,
sched, &lsin, &sin, "VERSION9");
if (QiDebug)
fprintf(stderr, "%s doing kerberos mutual authentication of %s with %s in realm %s.\n",
(status == KSUCCESS)?"success":"failure", NSSERVICE, UseHost, hrealm);
if (*Up) /* ???? */
dest_tkt(); /* destroy special tickets as soon as
possible */
/* get the final response (even if mutual failed)*/
for (;;)
{
if (!GetGood(MsgBuf, MAXSTR, FromQI))
{
fprintf(stderr, "Whoops--the nameserver died.\n");
return LR_ERROR;
}
code = atoi(MsgBuf);
if (code >= LR_OK) /* final response */
break;
}
if (*Up)
{
krb_set_tkt_string(okrbtkfile);
}
if (status == KSUCCESS && code == LR_OK) /* logged in */
{
memset(kpass,0,sizeof(kpass)); /* Don't need to fall thru */
strcpy(MyAlias, (char *)strchr(MsgBuf, ':') + 1);
*(char *)strchr(MyAlias, ':') = '\0';
} else
*MyAlias = '\0';
return (code);
}
}
#endif /*KERBEROS*/
#ifdef EMAIL_AUTH
/*
* Bean sprout recipe, using Berkeley r-command ingredients.
* (actually just calls LoginOriginal since I stole this code
* out of ph....)
*/
static int
LoginEmail(UseHost, ToQI, FromQI, Options, MyAlias, Up, Pp)
const char *UseHost;
FILE *ToQI, *FromQI;
int Options;
char *MyAlias, **Up, **Pp;
{
int rc;
LoginQiEmailAuth = 1; /* set our secret internal flag */
if (QiDebug)
fprintf(stderr,"attempting email login.\n");
rc = LoginOriginal(UseHost, ToQI, FromQI, Options, MyAlias, Up, Pp);
LoginQiEmailAuth = 0;
return rc;
}
#endif /*EMAIL_AUTH*/
/*
** LogoutQi - Logout from QI server.
**
** Parameters:
** ToQI - stream descriptor to write to
** FromQI - stream descriptor to read from
**
** Returns:
** success(LR_OK) or failure indication
**
*/
int
LogoutQi(ToQI, FromQI)
FILE *ToQI, *FromQI;
{
QIR *r;
int n;
fprintf(ToQI,"logout\n");
fflush(ToQI);
if ((r = ReadQi(FromQI, &n)) == NULL)
return LR_ERROR;
n = r->code;
/* Accept the memory leak to simplify standalone compilation of ph */
/* FreeQIR(r); */
return n;
}
/*
* get a non-comment line from a stream
* a comment is a line beginning with a # sign
*/
int
GetGood(str, maxc, fp)
char *str; /*space to put the chars */
int maxc; /*max # of chars we want */
#ifdef VMS
int fp; /*stream to read them from */
{
static char Qbuf[MAXSTR + 4] =
{'\0'};
static int pos =
{0}, end =
{0}, len =
{0};
char *linp;
for (;;)
{
if (pos >= len)
{
len = netread(fp, Qbuf, maxc);
if (len <= 0)
return (0);
Qbuf[len] = '\0';
pos = 0;
}
linp = strchr(Qbuf + pos, '\n'); /*find next newline char */
if (linp == NULL)
end = len; /*no newline chars left */
else
end = linp - Qbuf; /*convert pointer to index */
strncpy(str, Qbuf + pos, end - pos + 1);
*(str + end - pos + 1) = '\0';
pos = end + 1; /*save new position for next time */
if (!*str)
#else
FILE *fp; /*stream to read them from */
{
errno = 0;
for (;;)
{
if (! fgets(str, maxc, fp))
#endif
{
fputs("Oops; lost connection to server.\n", stderr);
if (errno)
perror("");
exit(1);
} else if (*str != '#')
{
if (QiDebug)
fprintf(stderr, "read =%s", str);
return (1); /*not a comment; success! */
}
}
/* NOTREACHED */
}