Index: TODO =================================================================== RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/TODO,v retrieving revision 1.53 diff -u -r1.53 TODO --- TODO 12 Jan 2003 23:00:34 -0000 1.53 +++ TODO 14 Jan 2003 09:18:01 -0000 @@ -15,8 +15,6 @@ - More platforms for for setproctitle() emulation (testing needed) -- Handle changing passwords for the non-PAM expired password case - - Improve PAM support (a pam_lastlog module will cause sshd to exit) and maybe support alternate forms of authentications like OPIE via pam? Index: acconfig.h =================================================================== RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/acconfig.h,v retrieving revision 1.149 diff -u -r1.149 acconfig.h --- acconfig.h 10 Mar 2003 00:38:10 -0000 1.149 +++ acconfig.h 10 Mar 2003 04:19:25 -0000 @@ -25,6 +25,9 @@ /* from environment and PATH */ #undef LOGIN_PROGRAM_FALLBACK +/* Path to passwd program */ +#undef PASSWD_PROGRAM_PATH + /* Define if your password has a pw_class field */ #undef HAVE_PW_CLASS_IN_PASSWD Index: auth-pam.c =================================================================== RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/auth-pam.c,v retrieving revision 1.55 diff -u -r1.55 auth-pam.c --- auth-pam.c 22 Jan 2003 04:42:26 -0000 1.55 +++ auth-pam.c 28 Feb 2003 23:27:10 -0000 @@ -42,8 +42,6 @@ #define NEW_AUTHTOK_MSG \ "Warning: Your password has expired, please change it now." -#define NEW_AUTHTOK_MSG_PRIVSEP \ - "Your password has expired, the session cannot proceed." static int do_pam_conversation(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr); @@ -60,7 +58,7 @@ /* states for do_pam_conversation() */ enum { INITIAL_LOGIN, OTHER } pamstate = INITIAL_LOGIN; /* remember whether pam_acct_mgmt() returned PAM_NEW_AUTHTOK_REQD */ -static int password_change_required = 0; +extern int password_change_required; /* remember whether the last pam_authenticate() succeeded or not */ static int was_authenticated = 0; @@ -248,18 +246,10 @@ case PAM_SUCCESS: /* This is what we want */ break; -#if 0 case PAM_NEW_AUTHTOK_REQD: - message_cat(&__pam_msg, use_privsep ? - NEW_AUTHTOK_MSG_PRIVSEP : NEW_AUTHTOK_MSG); - /* flag that password change is necessary */ - password_change_required = 1; - /* disallow other functionality for now */ - no_port_forwarding_flag |= 2; - no_agent_forwarding_flag |= 2; - no_x11_forwarding_flag |= 2; + message_cat(&__pam_msg, NEW_AUTHTOK_MSG); + flag_password_change_required(); break; -#endif default: log("PAM rejected by account configuration[%d]: " "%.200s", pam_retval, PAM_STRERROR(__pamh, @@ -345,13 +335,7 @@ fatal("PAM pam_chauthtok failed[%d]: %.200s", pam_retval, PAM_STRERROR(__pamh, pam_retval)); #if 0 - /* XXX: This would need to be done in the parent process, - * but there's currently no way to pass such request. */ - no_port_forwarding_flag &= ~2; - no_agent_forwarding_flag &= ~2; - no_x11_forwarding_flag &= ~2; - if (!no_port_forwarding_flag && options.allow_tcp_forwarding) - channel_permit_all_opens(); + flag_password_change_successful(); #endif } } Index: auth-passwd.c =================================================================== RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/auth-passwd.c,v retrieving revision 1.51 diff -u -r1.51 auth-passwd.c --- auth-passwd.c 29 Jan 2003 23:20:57 -0000 1.51 +++ auth-passwd.c 12 Mar 2003 08:20:53 -0000 @@ -42,6 +42,10 @@ #include "log.h" #include "servconf.h" #include "auth.h" +#include "buffer.h" +#include "misc.h" +#include "channels.h" +#include "auth-options.h" #if !defined(USE_PAM) && !defined(HAVE_OSF_SIA) /* Don't need any of these headers for the PAM or SIA cases */ @@ -81,9 +85,9 @@ #endif /* !USE_PAM && !HAVE_OSF_SIA */ extern ServerOptions options; -#ifdef WITH_AIXAUTHENTICATE -extern char *aixloginmsg; -#endif + +int password_change_required = 0; +pid_t password_change_pid; /* pid used to reset forwarding flags */ /* * Tries to authenticate the user using password. Returns true if @@ -148,15 +152,16 @@ # endif # ifdef WITH_AIXAUTHENTICATE authsuccess = (authenticate(pw->pw_name,password,&reenter,&authmsg) == 0); - + aix_remove_embedded_newlines(authmsg); if (authsuccess) - /* We don't have a pty yet, so just label the line as "ssh" */ - if (loginsuccess(authctxt->user, - get_canonical_hostname(options.verify_reverse_mapping), - "ssh", &aixloginmsg) < 0) - aixloginmsg = NULL; - - return(authsuccess); + debug3("AIX/authenticate succeeded for user %s: %.100s", + pw->pw_name, authmsg); + else + debug3("AIX/authenticate failed for user %s: %.100s", + pw->pw_name, authmsg); + if (authmsg) + xfree(authmsg); + return authsuccess; # endif # ifdef KRB4 if (options.kerberos_authentication == 1) { @@ -231,4 +236,79 @@ /* Authentication is accepted if the encrypted passwords are identical. */ return (strcmp(encrypted_password, pw_password) == 0); #endif /* !USE_PAM && !HAVE_OSF_SIA */ +} + +/* + * Perform generic password change via tty. Like do_pam_chauthtok(), + * it throws a fatal error if the password can't be changed. + */ +int +do_tty_change_password(struct passwd *pw) +{ + pid_t pid; + int status; + mysig_t old_signal; + + old_signal = mysignal(SIGCHLD, SIG_DFL); + + if ((pid = fork()) == -1) + fatal("Couldn't fork: %s", strerror(errno)); + + if (pid == 0) { + setuid(pw->pw_uid); + if (geteuid() == 0) + execl(PASSWD_PROGRAM_PATH, "passwd", pw->pw_name, + (char *)NULL); + else + execl(PASSWD_PROGRAM_PATH, "passwd", (char *)NULL); + + /* execl shouldn't return */ + fatal("Couldn't exec %s", PASSWD_PROGRAM_PATH); + exit(1); + } + + if (waitpid(pid, &status, 0) == -1) + fatal("Couldn't wait for child: %s", strerror(errno)); + mysignal(SIGCHLD, old_signal); + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + debug("%s password changed sucessfully", __func__); + flag_password_change_successful(); + return 1; + } else { + fatal("Failed to change password for %s, passwd returned %d", + pw->pw_name, status); + return 0; + } +} + +/* + * flag that password change is necessary + */ +void +flag_password_change_required(void) +{ + debug3("disabling forwarding"); + password_change_required = 1; + + /* disallow other functionality for now */ + no_port_forwarding_flag |= 2; + no_agent_forwarding_flag |= 2; + no_x11_forwarding_flag |= 2; +} + +/* + * password change successful + * XXX: must be done in parent, but currently there is no way to pass + * this request. + */ +void +flag_password_change_successful(void) +{ + debug3("resetting forwarding flags"); + + password_change_required = 0; + no_port_forwarding_flag &= ~2; + no_agent_forwarding_flag &= ~2; + no_x11_forwarding_flag &= ~2; } Index: auth.c =================================================================== RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/auth.c,v retrieving revision 1.67 diff -u -r1.67 auth.c --- auth.c 18 Jan 2003 05:24:06 -0000 1.67 +++ auth.c 12 Mar 2003 07:19:32 -0000 @@ -36,6 +36,11 @@ #include #endif +#ifdef WITH_AIXAUTHENTICATE +#include +#include +#endif + #include "xmalloc.h" #include "match.h" #include "groupaccess.h" @@ -51,9 +56,12 @@ #include "misc.h" #include "bufaux.h" #include "packet.h" +#include "sshlogin.h" /* import */ extern ServerOptions options; +extern Buffer expire_message; +extern Buffer login_message; /* Debugging messages */ Buffer auth_debug; @@ -75,9 +83,6 @@ const char *hostname = NULL, *ipaddr = NULL; char *shell; int i; -#ifdef WITH_AIXAUTHENTICATE - char *loginmsg; -#endif /* WITH_AIXAUTHENTICATE */ #if !defined(USE_PAM) && defined(HAVE_SHADOW_H) && \ !defined(DISABLE_SHADOW) && defined(HAS_SHADOW_EXPIRE) struct spwd *spw; @@ -88,35 +93,63 @@ if (!pw || !pw->pw_name) return 0; +#define DAY (24L * 60 * 60) /* 1 day in seconds */ +#define WEEK (DAY * 7) /* 1 week in seconds */ #if !defined(USE_PAM) && defined(HAVE_SHADOW_H) && \ !defined(DISABLE_SHADOW) && defined(HAS_SHADOW_EXPIRE) -#define DAY (24L * 60 * 60) /* 1 day in seconds */ if ((spw = getspnam(pw->pw_name)) != NULL) { + int daysleft; + today = time(NULL) / DAY; debug3("allowed_user: today %d sp_expire %d sp_lstchg %d" - " sp_max %d", (int)today, (int)spw->sp_expire, - (int)spw->sp_lstchg, (int)spw->sp_max); + " sp_max %d sp_warn %d", (int)today, (int)spw->sp_expire, + (int)spw->sp_lstchg, (int)spw->sp_max, (int)spw->sp_warn); /* * We assume account and password expiration occurs the * day after the day specified. */ - if (spw->sp_expire != -1 && today > spw->sp_expire) { + daysleft = spw->sp_expire - today; + if (spw->sp_expire == -1) { + debug3("account expiration disabled"); + } else if (today > spw->sp_expire) { log("Account %.100s has expired", pw->pw_name); return 0; - } + } else if (daysleft <= spw->sp_warn) { + char buf[256]; + debug3("account will expire in %d days", daysleft); + snprintf(buf, sizeof(buf), + "Your account will expire in %d day%s.\n", + daysleft, daysleft == 1 ? "" : "s"); + buffer_append(&login_message, buf, strlen(buf)); + } + +#define PWCHG_FORCED "You must change your password now.\n" +#define PWCHG_EXPIRED "Your password has expired, you must change it now.\n" + daysleft = spw->sp_lstchg + spw->sp_max - today; if (spw->sp_lstchg == 0) { log("User %.100s password has expired (root forced)", pw->pw_name); - return 0; - } - - if (spw->sp_max != -1 && - today > spw->sp_lstchg + spw->sp_max) { + flag_password_change_required(); + buffer_append(&expire_message, PWCHG_FORCED, + sizeof(PWCHG_FORCED)); + } else if (spw->sp_max == -1) { + debug3("password expiration disabled"); + } else if (daysleft < 0) { log("User %.100s password has expired (password aged)", pw->pw_name); - return 0; + flag_password_change_required(); + buffer_append(&expire_message, PWCHG_EXPIRED, + sizeof(PWCHG_EXPIRED)); + } else if (daysleft <= spw->sp_warn) { + char buf[256]; + + debug3("password will expire in %d days", daysleft); + snprintf(buf, sizeof(buf), + "Your password will expire in %d day%s.\n", + daysleft, daysleft == 1 ? "" : "s"); + buffer_append(&expire_message, buf, strlen(buf)); } } #endif @@ -206,26 +239,75 @@ * PermitRootLogin to control logins via ssh), or if running as * non-root user (since loginrestrictions will always fail). */ - if ((pw->pw_uid != 0) && (geteuid() == 0) && - loginrestrictions(pw->pw_name, S_RLOGIN, NULL, &loginmsg) != 0) { + if ( (pw->pw_uid != 0) && (geteuid() == 0) ) { int loginrestrict_errno = errno; + char *msg; - if (loginmsg && *loginmsg) { - /* Remove embedded newlines (if any) */ - char *p; - for (p = loginmsg; *p; p++) { - if (*p == '\n') - *p = ' '; + /* check for AIX account restrictions */ + if (loginrestrictions(pw->pw_name, S_RLOGIN, NULL, &msg) != 0) { + if (msg && *msg) { + aix_remove_embedded_newlines(msg); + log("Login restricted for %s: %.100s", + pw->pw_name, msg); + xfree(msg); } - /* Remove trailing newline */ - *--p = '\0'; - log("Login restricted for %s: %.100s", pw->pw_name, - loginmsg); - } - /* Don't fail if /etc/nologin set */ - if (!(loginrestrict_errno == EPERM && - stat(_PATH_NOLOGIN, &st) == 0)) - return 0; + + /* Don't fail if /etc/nologin set */ + if (!(loginrestrict_errno == EPERM && + stat(_PATH_NOLOGIN, &st) == 0)) + return 0; + } + } + + + /* + * Check AIX password expiry. Only check when running as root. + * Unpriv'ed users can't access /etc/security/passwd or + * /etc/security/user so passwdexpired will always fail. + */ + if (geteuid() == 0) { + char *msg; + int result, maxexpired; + struct userpw *upw; + + /* check if password expired too long */ + upw = getuserpw(pw->pw_name); + result = getuserattr(pw->pw_name, S_MAXEXPIRED, &maxexpired, + SEC_INT); + if (upw != NULL && result == 0) { + debug3("%s lastupdate %lu maxexpired %d wks time %d", + __func__, upw->upw_lastupdate, maxexpired, + (int)time(NULL)); + if (maxexpired != -1 && upw->upw_lastupdate + + (maxexpired*WEEK) <= time(NULL) ){ + log("User %.100s password expired too long", + pw->pw_name); + return 0; + } + } + + result = passwdexpired(pw->pw_name, &msg); + buffer_append(&expire_message, msg, strlen(msg)); + if (msg && *msg) + aix_remove_embedded_newlines(msg); + debug3("AIX/passwdexpired returned %d msg %.100s", result, msg); + + switch (result) { + case 0: /* success, password not expired */ + break; + case 1: /* expired, password change required */ + flag_password_change_required(); + break; + default: /* user can't change(2) or other error (-1) */ + log("Password can't be changed for user %s: " + "%.100s", pw->pw_name, msg); + if (msg) + xfree(msg); + return 0; + } + if (msg) + xfree(msg); + } #endif /* WITH_AIXAUTHENTICATE */ @@ -241,6 +323,45 @@ return authctxt; } +/* + * Generate last_login message and store for later display. This must be + * called before login_login() is called and lastlog is updated. + */ +void +generate_login_message(const char *user, uid_t uid, const char *host) +{ +#ifdef WITH_AIXAUTHENTICATE + char *msg; + + /* We don't have a pty yet, so just label the line as "ssh" */ + if (loginsuccess(user, host, "ssh", &msg) >= 0) + buffer_append(&login_message, msg, strlen(msg)); +#elif !defined(NO_SSH_LASTLOG) + if (options.print_lastlog) { + char *time_string, lasthost[MAXHOSTNAMELEN], buf[256]; + time_t last_login_time; + + last_login_time = get_last_login_time(uid, user, lasthost, + sizeof(lasthost)); + + if (last_login_time != 0) { + time_string = ctime(&last_login_time); + if (strchr(time_string, '\n')) + *strchr(time_string, '\n') = 0; + if (strcmp(lasthost, "") == 0) + snprintf(buf, sizeof(buf), + "Last login: %s\r\n", + time_string); + else + snprintf(buf, sizeof(buf), + "Last login: %s from %s\r\n", + time_string, lasthost); + buffer_append(&login_message, buf, strlen(buf)); + } + } +#endif +} + void auth_log(Authctxt *authctxt, int authenticated, char *method, char *info) { @@ -268,6 +389,9 @@ get_remote_port(), info); + if (authenticated && geteuid() == 0) + generate_login_message(authctxt->user, authctxt->pw->pw_uid, + get_canonical_hostname(options.verify_reverse_mapping)); #ifdef WITH_AIXAUTHENTICATE if (authenticated == 0 && strcmp(method, "password") == 0) loginfailed(authctxt->user, Index: auth.h =================================================================== RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/auth.h,v retrieving revision 1.44 diff -u -r1.44 auth.h --- auth.h 27 Sep 2002 03:26:01 -0000 1.44 +++ auth.h 12 Mar 2003 08:25:57 -0000 @@ -156,6 +156,9 @@ int allowed_user(struct passwd *); struct passwd * getpwnamallow(const char *user); +int do_tty_change_password(struct passwd *pw); +void flag_password_change_required(void); +void flag_password_change_successful(void); char *get_challenge(Authctxt *); int verify_response(Authctxt *, const char *); Index: configure.ac =================================================================== RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/configure.ac,v retrieving revision 1.110 diff -u -r1.110 configure.ac --- configure.ac 10 Mar 2003 00:38:10 -0000 1.110 +++ configure.ac 10 Mar 2003 04:19:26 -0000 @@ -41,6 +41,13 @@ fi fi +AC_PATH_PROG(PASSWD_PROGRAM_PATH, passwd) +if test ! -z "$PASSWD_PROGRAM_PATH" ; then + AC_DEFINE_UNQUOTED(PASSWD_PROGRAM_PATH, "$PASSWD_PROGRAM_PATH") +else + AC_MSG_ERROR([*** passwd command not found - check config.log ***]) +fi + if test -z "$LD" ; then LD=$CC fi Index: session.c =================================================================== RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/session.c,v retrieving revision 1.231 diff -u -r1.231 session.c --- session.c 10 Mar 2003 00:21:18 -0000 1.231 +++ session.c 12 Mar 2003 07:26:05 -0000 @@ -95,6 +95,9 @@ extern u_int utmp_len; extern int startup_pipe; extern void destroy_sensitive_data(void); +extern Buffer expire_message; +extern Buffer login_message; +extern int password_change_required; /* original command from peer. */ const char *original_command = NULL; @@ -103,10 +106,6 @@ #define MAX_SESSIONS 10 Session sessions[MAX_SESSIONS]; -#ifdef WITH_AIXAUTHENTICATE -char *aixloginmsg; -#endif /* WITH_AIXAUTHENTICATE */ - #ifdef HAVE_LOGIN_CAP login_cap_t *lc; #endif @@ -458,10 +457,11 @@ #if defined(USE_PAM) do_pam_session(s->pw->pw_name, NULL); do_pam_setcred(1); - if (is_pam_password_change_required()) +#endif /* USE_PAM */ + + if (password_change_required) packet_disconnect("Password change required but no " "TTY available"); -#endif /* USE_PAM */ /* Fork the child. */ if ((pid = fork()) == 0) { @@ -721,10 +721,10 @@ void do_login(Session *s, const char *command) { - char *time_string; socklen_t fromlen; struct sockaddr_storage from; struct passwd * pw = s->pw; + int password_changed = 0; pid_t pid = getpid(); /* @@ -748,16 +748,24 @@ options.verify_reverse_mapping), (struct sockaddr *)&from, fromlen); -#ifdef USE_PAM /* * If password change is needed, do it now. * This needs to occur before the ~/.hushlogin check. */ - if (is_pam_password_change_required()) { + buffer_append(&expire_message, "\0", 1); + if (password_change_required) { +#ifdef USE_PAM print_pam_messages(); - do_pam_chauthtok(); - } + if (use_privsep) + do_pam_chauthtok(); + else + do_tty_change_password(pw); +#else + printf("%s", (char *)buffer_ptr(&expire_message)); + do_tty_change_password(pw); #endif + password_changed = 1; + } if (check_quietlogin(s, command)) return; @@ -765,24 +773,14 @@ #ifdef USE_PAM if (!is_pam_password_change_required()) print_pam_messages(); +#else + if (!password_changed) + printf("%s", (char *)buffer_ptr(&expire_message)); #endif /* USE_PAM */ -#ifdef WITH_AIXAUTHENTICATE - if (aixloginmsg && *aixloginmsg) - printf("%s\n", aixloginmsg); -#endif /* WITH_AIXAUTHENTICATE */ - -#ifndef NO_SSH_LASTLOG - if (options.print_lastlog && s->last_login_time != 0) { - time_string = ctime(&s->last_login_time); - if (strchr(time_string, '\n')) - *strchr(time_string, '\n') = 0; - if (strcmp(s->hostname, "") == 0) - printf("Last login: %s\r\n", time_string); - else - printf("Last login: %s from %s\r\n", time_string, - s->hostname); - } -#endif /* NO_SSH_LASTLOG */ + + /* display post-login message */ + buffer_append(&login_message, "\0", 1); + printf("%s", (char *)buffer_ptr(&login_message)); do_motd(); } @@ -1603,12 +1601,6 @@ if (s->ttyfd != -1) { packet_disconnect("Protocol error: you already have a pty."); return 0; - } - /* Get the time and hostname when the user last logged in. */ - if (options.print_lastlog) { - s->hostname[0] = '\0'; - s->last_login_time = get_last_login_time(s->pw->pw_uid, - s->pw->pw_name, s->hostname, sizeof(s->hostname)); } s->term = packet_get_string(&len); Index: session.h =================================================================== RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/session.h,v retrieving revision 1.23 diff -u -r1.23 session.h --- session.h 4 Jul 2002 00:14:18 -0000 1.23 +++ session.h 1 Feb 2003 07:22:08 -0000 @@ -39,9 +39,6 @@ int ptyfd, ttyfd, ptymaster; u_int row, col, xpixel, ypixel; char tty[TTYSZ]; - /* last login */ - char hostname[MAXHOSTNAMELEN]; - time_t last_login_time; /* X11 */ u_int display_number; char *display; Index: sshd.c =================================================================== RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/sshd.c,v retrieving revision 1.236 diff -u -r1.236 sshd.c --- sshd.c 10 Mar 2003 00:38:10 -0000 1.236 +++ sshd.c 10 Mar 2003 04:19:28 -0000 @@ -205,6 +205,9 @@ int use_privsep; struct monitor *pmonitor; +Buffer expire_message; /* "password will expire/has expired" messages */ +Buffer login_message; /* message to be displayed after login */ + /* Prototypes for various functions defined later in this file. */ void destroy_sensitive_data(void); void demote_sensitive_data(void); @@ -1505,6 +1508,10 @@ if (use_privsep) if ((authctxt = privsep_preauth()) != NULL) goto authenticated; + + /* prepare buffers to collect authentication/expiry messages */ + buffer_init(&login_message); + buffer_init(&expire_message); /* perform the key exchange */ /* authenticate user and start session */ Index: version.h =================================================================== RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/version.h,v retrieving revision 1.41 diff -u -r1.41 version.h --- version.h 3 Oct 2002 01:55:38 -0000 1.41 +++ version.h 12 Mar 2003 07:08:51 -0000 @@ -1,4 +1,4 @@ /* $OpenBSD: version.h,v 1.35 2002/10/01 13:24:50 markus Exp $ */ -#define SSH_VERSION "OpenSSH_3.5p1" +#define SSH_VERSION "OpenSSH_3.5p1-pwexp18" Index: openbsd-compat/port-aix.c =================================================================== RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/openbsd-compat/port-aix.c,v retrieving revision 1.6 diff -u -r1.6 port-aix.c --- openbsd-compat/port-aix.c 7 Jul 2002 02:17:36 -0000 1.6 +++ openbsd-compat/port-aix.c 14 Jan 2003 22:14:22 -0000 @@ -52,5 +52,25 @@ xfree(cp); } -#endif /* _AIX */ +#ifdef WITH_AIXAUTHENTICATE +/* + * Remove embedded newlines in string (if any). + * Used before logging messages returned by AIX authentication functions + * so the message is logged on one line. + */ +void +aix_remove_embedded_newlines(char *p) +{ + if (p == NULL) + return; + + for (; *p; p++) { + if (*p == '\n') + *p = ' '; + } + /* Remove trailing newline */ + *--p = '\0'; +} +#endif /* WITH_AIXAUTHENTICATE */ +#endif /* _AIX */ Index: openbsd-compat/port-aix.h =================================================================== RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/openbsd-compat/port-aix.h,v retrieving revision 1.7 diff -u -r1.7 port-aix.h --- openbsd-compat/port-aix.h 1 Feb 2003 04:43:35 -0000 1.7 +++ openbsd-compat/port-aix.h 1 Feb 2003 05:45:45 -0000 @@ -37,4 +37,5 @@ #endif void aix_usrinfo(struct passwd *pw); +void aix_remove_embedded_newlines(char *); #endif /* _AIX */