/* * Copyright (c) 1992, 1993 by the University of Southern California * * For copying and distribution information, please see the file * * * Written by swa 1992 to provide common code for starting request * Modified by prasad 1992 to add Kerberos support * Modified by bcn 1/93 to use new ardp library and rreq structure * Modified by swa 6/93 to construct multiple-packet messages. */ #include #include #include #include #include #include #include #include #include #include extern int h_errno; /* For compatability between GCC-2.3.1 running under SunOS 4.1 and the sunOS * standard include files (they define size_t and others differently), we MUST * include stddef and stdarg after anything that might include the sun include * file (in this case, includes ). */ #include #include #include #include /* must precede pfs.h for defs. of P_KERBEROS, */ /* P_P_PASSWORD */ #include #include #include #include #ifdef P_KERBEROS #include #endif /* P_KERBEROS */ /* gives us a name for this buffer, which we need so we can keep the reference around. */ /* Ok that this is static, since it's read-only. */ static char pfs_sw_id_buf[] = PFS_SW_ID; static char *pfs_sw_id = pfs_sw_id_buf; /* Oops - I dont think a "char p__username" is very usefull! - Mitra*/ char *p__username = NULL; extern int pfs_debug; static PAUTH p__get_pauth(int type, const char *hostname); #ifdef P_P_PASSWORD char *ppw_encode(); #endif /* P_P_PASSWORD */ char *get_psession_filename(); static char *uid_to_name(); #ifdef P_ACCOUNT static void p__add_acc_req(); #endif /* This code handles possible multiple resettings of the software ID without any memory leaks. It is internal; should only be called through p_initialize(). Nothing will break if you ignore this admonition, but it keeps the library interface smaller. */ void p__set_sw_id(char *app_sw_id) { assert(P_IS_THIS_THREAD_MASTER()); if (app_sw_id && *app_sw_id) { if (pfs_sw_id == pfs_sw_id_buf) pfs_sw_id = qsprintf_stcopyr(NULL,"%s(%s)",app_sw_id,PFS_SW_ID); else pfs_sw_id = qsprintf_stcopyr(pfs_sw_id,"%s(%s)",app_sw_id, PFS_SW_ID); } else { /* Resetting software ID to normal values. */ if (pfs_sw_id == pfs_sw_id_buf) ; /* Same as before; leave set to default. */ else { stfree(pfs_sw_id); /* must be set to some allocated memory. */ pfs_sw_id = pfs_sw_id_buf; } } } /* * p__start_req - start a prospero request * * p__start_req takes the name of the server (used by Kerberos to * determine what credentials are needed), it allocates a request * structure, and it fills in the start, including the Prospero * version and software ID, and authentication information. The * function returns a request of type RREQ, which can be used * in subsequent calls to p__addreq, and passed to ardp_send. */ RREQ p__start_req(const char server_hostname[]) { RREQ req = ardp_rqalloc(); /* The request to fill in */ PAUTH authinfo; /* Structure containing authentication info */ PAUTH authp; /* To iterate through authinfo list */ TOKEN prin; /* To Iterate through principal names */ #ifdef CLIENTS_REQUEST_VERSION_FROM_SERVER p__add_req(req, "VERSION\n"); #endif p__add_req(req, "VERSION %d %s", VFPROT_VNO, pfs_sw_id); authinfo = p__get_pauth(PFSA_UNAUTHENTICATED, server_hostname); if (authinfo) { p__add_req(req, "\nAUTHENTICATE '' UNAUTHENTICATED %'s", authinfo->authenticator); /* For the UNAUTHENTICATED and KERBEROS authentication types, */ /* the principals are optional additional information for */ /* debugging use. We don't worry about them here. */ for(prin = authinfo->principals; prin; prin = prin->next) p__add_req(req, " %'s", prin->token); pafree(authinfo); } #ifdef P_KERBEROS authinfo = p__get_pauth(PFSA_KERBEROS, server_hostname); for (authp = authinfo; authp; authp = authp->next) { p__add_req(req, "\nAUTHENTICATE '' KERBEROS %'s", authp->authenticator); /* For the UNAUTHENTICATED and KERBEROS authentication types, the principals are optional additional information for debugging use. We don't worry about them here. */ for (prin = authp->principals; prin; prin = prin->next) p__add_req(req, " %'s", prin->token); } if (authinfo) palfree(authinfo); #endif #ifdef P_P_PASSWORD authinfo = p__get_pauth(PFSA_P_PASSWORD, server_hostname); for (authp = authinfo; authp; authp = authp->next) { p__add_req(req, "\nAUTHENTICATE OPTIONAL P_PASSWORD %'s", authp->authenticator); /* Principal NOT optional here! */ for (prin = authp->principals; prin; prin = prin->next) p__add_req(req, " %'s", prin->token); } if (authinfo) palfree(authinfo); #endif p__add_req(req, "\n"); #ifdef P_ACCOUNT p__add_acc_req(req, server_hostname); #endif return(req); } /* Adds stuff to a Prospero request. */ int p__add_req(RREQ req, const char fmt[], ...) { int retval; va_list ap; va_start(ap,fmt); retval = vp__add_req(req, fmt, ap); va_end(ap); return(retval); } int vp__add_req(RREQ req, const char fmt[], va_list ap) { PTEXT ptmp; /* packet being written to. */ int mesglen; /* # of characters in this message, NOT including trailing NUL which doesn't need to be sent. */ AUTOSTAT_CHARPP(bufp); /* buffer for long packets */ int numsent; /* How many chars. out of buf have been sent so far? */ if(!req->outpkt) { ptmp = ardp_ptalloc(); if(!ptmp) RETURNPFAILURE; APPEND_ITEM(ptmp,req->outpkt); } else { ptmp = req->outpkt->previous; /* last member of list. */ } /* Subtract 1: Trailing NUL character isn't sent, since the length of the message is encoded into the message. Recipient will automatically append the NUL byte for convenience of the users. */ /* Add one to buffer space available, since it includes an MBZ byte to catch runaway strings. vqsprintf() will always null-terminate the strings it writes. */ mesglen = vqsprintf(ptmp->ioptr, ARDP_PTXT_LEN + 1 - ptmp->length, fmt, ap) - 1; if (ptmp->length + mesglen <= ARDP_PTXT_LEN) { ptmp->length += mesglen; ptmp->ioptr = ptmp->start + ptmp->length; return(PSUCCESS); } /* Ran out of room. Have to write it to a temporary buffer, then copy. */ /* Four-line Optimization: we already know we're going to need mesglen + 1 of buffer space. By doing this, we save a possible double calling of vqsprintf() in p__vqbstprintf_stcopyr(). */ if (p__bstsize(*bufp) < mesglen + 1) { stfree(*bufp); *bufp = stalloc(mesglen + 1); } /* There used to be a lot of code here that did this in a slightly more efficient way, but I am willing to suffer a performance penalty in order to make this work more transparently. */ *bufp = p__vqbstprintf_stcopyr(*bufp, fmt, ap); assert(mesglen == p_bstlen(*bufp)); /* Record what we already sent. */ numsent = ARDP_PTXT_LEN - ptmp->length; ptmp->length = ARDP_PTXT_LEN; /* Start this loop with the current ptmp full of as much data as it can hold. */ while (numsent < mesglen) { ptmp = ardp_ptalloc(); if(!ptmp) RETURNPFAILURE; APPEND_ITEM(ptmp,req->outpkt); ptmp->length = ( mesglen - numsent > ARDP_PTXT_LEN ? ARDP_PTXT_LEN : mesglen - numsent); strncpy(ptmp->start, *bufp + numsent, ptmp->length); ptmp->ioptr = ptmp->start + ptmp->length; numsent += ptmp->length; } return PSUCCESS; } void p__set_username(char *un) { p__username = stcopyr(un, p__username); } static PAUTH p__get_pauth(int type, const char server_hostname[]) { #ifdef P_KERBEROS krb5_data buf; krb5_checksum send_cksum; krb5_ccache ccdef; krb5_principal server; static char *tkt_file = NULL; int retval = 0; #endif #ifdef P_P_PASSWORD PAUTH curr_auth = NULL; static char *password = NULL; typedef struct _p_passwd { char *hostname; char *princ_name; char *password; struct _p_passwd *next; } p_passwd; p_passwd *p_list = NULL, *p_ptr; #endif PAUTH auth = paalloc(); int ruid, euid; FILE *psession_fp; char *psession_filename = get_psession_filename(); char line[255]; AUTOSTAT_CHARPP(hostnamep); AUTOSTAT_CHARPP(princ_namep); int num, entry_exists = 0; char full_hname[255]; struct hostent *host; char *cp; char *hostaddr = NULL; struct in_addr haddr; #if defined(P_KERBEROS) || defined(P_P_PASSWORD) assert(P_IS_THIS_THREAD_MASTER()); /* Not thread safe yet */ #endif CHECK_MEM(); auth->principals = NULL; /* not really needed. Let's be paranoid. */ /* Convert hostname into canonical form */ qsprintf(full_hname, sizeof full_hname, "%s", server_hostname); /* Strip off the trailing (portnum), if any */ for (cp = full_hname; *cp; ++cp) ; if (cp > full_hname && *--cp == ')') { while (*--cp && cp >= full_hname) { if (*cp == '(') { *cp = '\0'; break; } if (!isdigit(*cp)) break; } } /* Look up server host */ /* Not multi-threaded yet because clients not done yet. */ CHECK_MEM(); assert(P_IS_THIS_THREAD_MASTER()); if ((host = gethostbyname(full_hname)) == (struct hostent *) 0) { if (pfs_debug) fprintf(stderr, "p__get_pauth(): unknown host %s\n", full_hname); pafree(auth); return NULL; } qsprintf(full_hname, sizeof full_hname, "%s", host->h_name); bcopy(host->h_addr,&haddr,sizeof(haddr)); hostaddr = stcopy(inet_ntoa(haddr)); switch(auth->ainfo_type = type) { case PFSA_UNAUTHENTICATED: if (p__username) { auth->authenticator = stcopy(p__username); return auth; } /* Upper-case to use in search of password file */ for (cp = full_hname; *cp; cp++) if (islower(*cp)) *cp = toupper(*cp); /* Get the real and effective user ids */ ruid = getuid(); euid = geteuid(); /* check if the psession file exists */ if (!(psession_fp = fopen(psession_filename, "r"))) /* No file; send real uid across */ auth->authenticator = uid_to_name(ruid); else { while (fgets(line, sizeof(line), psession_fp)) { num = qsscanf(line, "AUTHENTICATE %'&s UNAUTHENTICATED %'&s\n", &(*hostnamep), &(*princ_namep)); if (num < 2) continue; if (wcmatch(full_hname, (*hostnamep)) || strequal(hostaddr, (*hostnamep))) { entry_exists = 1; break; } } fclose(psession_fp); /* No entry in file; */ /* no UNAUTHENTICATED authentication sent */ if (!entry_exists) { pafree(auth); return NULL; } if ((*princ_namep)[0] && (!ruid || euid)) #ifdef OVERRIDE_DEFAULT_USERNAME auth->authenticator = stcopy((*princ_namep)); #else auth->authenticator = uid_to_name(ruid); #endif else auth->authenticator = uid_to_name(ruid); } return(auth); break; #ifdef P_KERBEROS /* 4/5/93 Made error handling pass errors to higher layers. */ case PFSA_KERBEROS: if (!(psession_fp = fopen(psession_filename, "r"))) { /* No file; do not send KERBEROS authentication */ pafree(auth); return NULL; } while (fgets(line, sizeof(line), psession_fp)) { num = qsscanf(line, "AUTHENTICATE %'&s KERBEROS %'&s %'&s\n", &(*hostnamep), &(*princ_namep), &tkt_file); if (num < 3) continue; if (wcmatch(full_hname, (*hostnamep)) || strequal(hostaddr, (*hostnamep))) { entry_exists = 1; break; } } fclose(psession_fp); if (!entry_exists) { /* No entry; do not send KERBEROS authentication */ pafree(auth); return NULL; } /* If ticket-file name not empty, tell KRB5 routines to look */ /* at it. */ if (tkt_file[0]) setenv("KRB5CCNAME", tkt_file, 1); /* PREPARE KRB_AP_REQ MESSAGE */ /* compute checksum, using CRC-32 */ if (!(send_cksum.contents = (krb5_octet *) stalloc(krb5_checksum_size(CKSUMTYPE_CRC32)))) { if (pfs_debug) fprintf(stderr, "p__get_pauth(): Cannot allocate Kerberos checksum\n"); pafree(auth); return NULL; } /* choose some random stuff to compute checksum from */ if (retval = krb5_calculate_checksum(CKSUMTYPE_CRC32, (char *) server_hostname, strlen(server_hostname), 0, 0, /* if length is 0, */ /* crc-32 doesn't use */ /* the seed */ &send_cksum)) { if (pfs_debug) fprintf(stderr, "p__get_pauth(): Error while computing Kerberos checksum\n"); stfree(send_cksum.contents); pafree(auth); return NULL; } /* Get credentials for server, create krb_mk_req message */ if (retval = krb5_cc_default(&ccdef)) { if (pfs_debug) fprintf(stderr, "p__get_pauth(): Error while getting default \ Kerberos cache\n"); stfree(send_cksum.contents); pafree(auth); return NULL; } /* Format service name into a principal structure and */ /* canonicalizes host name. */ if (retval = krb5_sname_to_principal(full_hname, KERBEROS_SERVICE, 1, /* Canonicalize */ &server)) { if (pfs_debug) fprintf(stderr, "p__get_pauth(): Error while setting up \ Kerberos server principal\n"); stfree(send_cksum.contents); pafree(auth); return NULL; } if (retval = krb5_mk_req(server, 0 /* default options */, &send_cksum, ccdef, &buf)) { if (pfs_debug) fprintf(stderr, "p__get_pauth(): Error while \ preparing Kerberos AP_REQ\n"); pafree(auth); stfree(send_cksum.contents); return NULL; } stfree(send_cksum.contents); auth->authenticator = stalloc(AUTHENTICATOR_SZ); retval = binencode(buf.data, buf.length, auth->authenticator, AUTHENTICATOR_SZ); if (retval >= AUTHENTICATOR_SZ) { internal_error("binencode: Buffer too small"); } return(auth); break; #endif /* P_KERBEROS */ #ifdef P_P_PASSWORD case PFSA_P_PASSWORD: /* Upper-case to use in search of password file */ for (cp = full_hname; *cp; cp++) if (islower(*cp)) *cp = toupper(*cp); if (!(psession_fp = fopen(psession_filename, "r"))) { #if 0 /* This is annoying. It's NOT AN ERROR for somebody not to have set up a session file, at least not right now. --swa, 9 May 1994 */ if (pfs_debug) fprintf(stderr, "p__get_pauth(): Error in P_PASSWORD \ authentication: Cannot open psession file %s\n", psession_filename); #endif pafree(auth); return NULL; } while (fgets(line, sizeof(line), psession_fp)) { if (line[0] == '#') continue; num = qsscanf(line, "AUTHENTICATE %'&s P_PASSWORD %'&s %'&s\n", &(*hostnamep), &(*princ_namep), &password); if (num < 3) /* Ignore if no password */ continue; if (wcmatch(full_hname, (*hostnamep)) || strequal(hostaddr, (*hostnamep))) { p_passwd *last = NULL; for (p_ptr = p_list; p_ptr; last = p_ptr, p_ptr = p_ptr->next) if (!strcmp((*princ_namep), p_ptr->princ_name)) { if (isdigit(p_ptr->hostname[0])) break; /* Already have most specific */ /* address */ if (isdigit((*hostnamep)[0]) || strlen((*hostnamep)) > strlen(p_ptr->hostname)) { p_ptr->hostname = stcopyr((*hostnamep), p_ptr->hostname); p_ptr->password = stcopyr(password, p_ptr->password); } break; } if (!p_ptr) { if (!last) { p_list = (p_passwd *) stalloc(sizeof(p_passwd)); last = p_list; } else { last->next = (p_passwd *) stalloc(sizeof(p_passwd)); last = last->next; } last->hostname = stcopy((*hostnamep)); last->princ_name = stcopy((*princ_namep)); last->password = stcopy(password); last->next = NULL; } } } (void) fclose(psession_fp); pafree(auth); auth = NULL; for (p_ptr = p_list; p_ptr; p_ptr = p_ptr->next) { if (!auth) auth = curr_auth = paalloc(); else { curr_auth->next = paalloc(); curr_auth = curr_auth->next; } curr_auth->ainfo_type = type; curr_auth->authenticator = stcopy(ppw_encode(p_ptr->password)); curr_auth->principals = tkalloc(p_ptr->princ_name); curr_auth->next = NULL; } return(auth); break; #endif /* P_P_PASSWORD */ default: fprintf(stderr, "p__get_pauth(): Unknown authenticator type: %d\n", type); internal_error("Unknown authenticator type!"); /* NOTREACHED */ } assert(FALSE); /*Unreached*/ return(NULL); /* To keep GCC happy*/ } #ifdef P_ACCOUNT static void p__add_acc_req(RREQ req, char *server_hostname) { FILE *psession_fp; char *psession_filename = get_psession_filename(); char line[255]; static char *hostname = NULL, *acc_method = NULL; static char *acc_server = NULL, *acc_name = NULL; static char *acc_verifier = NULL, *int_bill_ref = NULL; static char *currency = NULL, *prompt_amt = NULL, *max_amt = NULL; struct hostent *host; char *cp; char *hostaddr = NULL; struct in_addr haddr; int num, entry_exists = 0; char full_hname[255]; assert(P_IS_THIS_THREAD_MASTER()); /* Not thread safe yet */ /* Convert hostname into canonical form */ qsprintf(full_hname, sizeof full_hname, "%s", server_hostname); /* Strip off the trailing (portnum), if any */ for (cp = full_hname; *cp; ++cp) ; if (cp > full_hname && *--cp == ')') { while (*--cp && cp >= full_hname) { if (*cp == '(') { *cp = '\0'; break; } if (!isdigit(*cp)) break; } } /* Look up server host */ assert(P_IS_THIS_THREAD_MASTER()); if ((host = gethostbyname(full_hname)) == (struct hostent *) 0) { if (pfs_debug) fprintf(stderr, "p__add_acc_req(): unknown host %s\n", full_hname); return; } qsprintf(full_hname, sizeof full_hname, "%s", host->h_name); bcopy(host->h_addr,&haddr,sizeof(haddr)); hostaddr = stcopy(inet_ntoa(haddr)); /* Upper-case to use in search of password file */ for (cp = full_hname; *cp; cp++) if (islower(*cp)) *cp = toupper(*cp); if (!(psession_fp = fopen(psession_filename, "r"))) return; while (fgets(line, sizeof(line), psession_fp)) { num = qsscanf(line, "ACCOUNT %'&s %'&s %'&s %'&s %'&s %'&s\n", &hostname, &acc_method, &acc_server, &acc_name, &acc_verifier, &int_bill_ref); if (num < 6) continue; if (wcmatch(full_hname, hostname) || strequal(hostaddr, hostname)) { p__add_req(req, "ACCOUNT MANDATORY %'s %'s %'s %'s %'s", acc_method, acc_server, acc_name, ppw_encode(acc_verifier), int_bill_ref); while(fgets(line, sizeof(line), psession_fp) && strnequal(line, "CURRENCY", strlen("CURRENCY"))) { num = qsscanf(line, "CURRENCY %'&s %'&s %'&s\n", ¤cy, &prompt_amt, &max_amt); if (num < 2) continue; p__add_req(req, " %'s %'s", currency, prompt_amt); } p__add_req(req, "\n"); break; } } fclose(psession_fp); } #endif /* P_ACCOUNT */ /* returns name of user's psession file. */ char *get_psession_filename() { int uid; char *home; static char *psession_filename = NULL; assert(P_IS_THIS_THREAD_MASTER()); /* Not thread safe yet */ if (!(psession_filename = stcopy(getenv("PSESSION")))) { uid = getuid(); if (!(home = getenv("HOME"))) { if (pfs_debug) fprintf(stderr, "ERROR: Could not get value of HOME!\n"); return NULL; } psession_filename = qsprintf_stcopyr(psession_filename, "%s/.psession_%d", home, uid); } return psession_filename; } /* Converts a uid into a username. If unknown uid, returns a string of */ /* the form "uid#" */ static char *uid_to_name(int uid) { struct passwd *whoiampw; assert(P_IS_THIS_THREAD_MASTER()); /*getpwuid MT-Unsafe*/ DISABLE_PFS(whoiampw = getpwuid(uid)); if (whoiampw == 0) { char tmp_uid_str[100]; qsprintf(tmp_uid_str, sizeof tmp_uid_str, "uid#%d", uid); return stcopy(tmp_uid_str); } else { return stcopy(whoiampw->pw_name); } } /* Two-way function to encode string.*/ char *ppw_encode(char *str) { static char *retstr = NULL; int i; assert(P_IS_THIS_THREAD_MASTER()); /* Not thread safe yet */ retstr = stcopyr(str, retstr); for (i = 0; retstr[i]; i++) if (retstr[i] > 31 && retstr[i] < 127) retstr[i] = 126 - retstr[i] + 32; return retstr; }