- djm@cvs.openbsd.org 2005/03/01 10:42:49
[ssh-keygen.1 ssh-keygen.c ssh_config.5] add tools for managing known_hosts files with hashed hostnames, including hashing existing files and deleting hosts by name; ok markus@ deraadt@
This commit is contained in:
parent
db7b8171ee
commit
4b42d7f195
|
@ -36,6 +36,10 @@
|
||||||
- djm@cvs.openbsd.org 2005/03/01 10:41:28
|
- djm@cvs.openbsd.org 2005/03/01 10:41:28
|
||||||
[ssh-keyscan.1 ssh-keyscan.c]
|
[ssh-keyscan.1 ssh-keyscan.c]
|
||||||
option to hash hostnames output by ssh-keyscan; ok markus@ deraadt@
|
option to hash hostnames output by ssh-keyscan; ok markus@ deraadt@
|
||||||
|
- djm@cvs.openbsd.org 2005/03/01 10:42:49
|
||||||
|
[ssh-keygen.1 ssh-keygen.c ssh_config.5]
|
||||||
|
add tools for managing known_hosts files with hashed hostnames, including
|
||||||
|
hashing existing files and deleting hosts by name; ok markus@ deraadt@
|
||||||
|
|
||||||
20050226
|
20050226
|
||||||
- (dtucker) [openbsd-compat/bsd-openpty.c openbsd-compat/inet_ntop.c]
|
- (dtucker) [openbsd-compat/bsd-openpty.c openbsd-compat/inet_ntop.c]
|
||||||
|
@ -2212,4 +2216,4 @@
|
||||||
- (djm) Trim deprecated options from INSTALL. Mention UsePAM
|
- (djm) Trim deprecated options from INSTALL. Mention UsePAM
|
||||||
- (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu
|
- (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu
|
||||||
|
|
||||||
$Id: ChangeLog,v 1.3674 2005/03/01 10:48:03 djm Exp $
|
$Id: ChangeLog,v 1.3675 2005/03/01 10:48:35 djm Exp $
|
||||||
|
|
43
ssh-keygen.1
43
ssh-keygen.1
|
@ -1,4 +1,4 @@
|
||||||
.\" $OpenBSD: ssh-keygen.1,v 1.63 2004/08/13 00:01:43 jmc Exp $
|
.\" $OpenBSD: ssh-keygen.1,v 1.64 2005/03/01 10:42:49 djm Exp $
|
||||||
.\"
|
.\"
|
||||||
.\" -*- nroff -*-
|
.\" -*- nroff -*-
|
||||||
.\"
|
.\"
|
||||||
|
@ -81,6 +81,15 @@
|
||||||
.Nm ssh-keygen
|
.Nm ssh-keygen
|
||||||
.Fl D Ar reader
|
.Fl D Ar reader
|
||||||
.Nm ssh-keygen
|
.Nm ssh-keygen
|
||||||
|
.Fl F Ar hostname
|
||||||
|
.Op Fl f Ar known_hosts_file
|
||||||
|
.Nm ssh-keygen
|
||||||
|
.Fl H
|
||||||
|
.Op Fl f Ar known_hosts_file
|
||||||
|
.Nm ssh-keygen
|
||||||
|
.Fl R Ar hostname
|
||||||
|
.Op Fl f Ar known_hosts_file
|
||||||
|
.Nm ssh-keygen
|
||||||
.Fl U Ar reader
|
.Fl U Ar reader
|
||||||
.Op Fl f Ar input_keyfile
|
.Op Fl f Ar input_keyfile
|
||||||
.Nm ssh-keygen
|
.Nm ssh-keygen
|
||||||
|
@ -243,6 +252,38 @@ Provides the new comment.
|
||||||
.It Fl D Ar reader
|
.It Fl D Ar reader
|
||||||
Download the RSA public key stored in the smartcard in
|
Download the RSA public key stored in the smartcard in
|
||||||
.Ar reader .
|
.Ar reader .
|
||||||
|
.It Fl F Ar hostname
|
||||||
|
Search for the specified
|
||||||
|
.Ar hostname
|
||||||
|
in a
|
||||||
|
.Pa known_hosts
|
||||||
|
file, listing any occurances found.
|
||||||
|
This option is useful to find hashed host names or addresses and may also be
|
||||||
|
used in conjunction with the
|
||||||
|
.Fl H
|
||||||
|
option to print found keys in a hashed format.
|
||||||
|
.It Fl H
|
||||||
|
Hash a
|
||||||
|
.Pa known_hosts
|
||||||
|
file, printing the result to standard output.
|
||||||
|
This replaces all hostnames and addresses with hashed representations.
|
||||||
|
These hashes may be used normally by
|
||||||
|
.Nm ssh
|
||||||
|
and
|
||||||
|
.Nm sshd ,
|
||||||
|
but they do not reveal identifying information should the file's contents
|
||||||
|
be disclosed.
|
||||||
|
This option will not modify existing hashed hostnames and is therefore safe
|
||||||
|
to use on files that mix hashed and non-hashed names.
|
||||||
|
.It Fl R Ar hostname
|
||||||
|
Removes all keys belonging to
|
||||||
|
.Ar hostname
|
||||||
|
from a
|
||||||
|
.Pa known_hosts
|
||||||
|
file.
|
||||||
|
This option is useful to delete hashed hosts (see the
|
||||||
|
.Fl H
|
||||||
|
option above).
|
||||||
.It Fl G Ar output_file
|
.It Fl G Ar output_file
|
||||||
Generate candidate primes for DH-GEX.
|
Generate candidate primes for DH-GEX.
|
||||||
These primes must be screened for
|
These primes must be screened for
|
||||||
|
|
228
ssh-keygen.c
228
ssh-keygen.c
|
@ -12,7 +12,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "includes.h"
|
#include "includes.h"
|
||||||
RCSID("$OpenBSD: ssh-keygen.c,v 1.118 2004/12/23 17:38:07 markus Exp $");
|
RCSID("$OpenBSD: ssh-keygen.c,v 1.119 2005/03/01 10:42:49 djm Exp $");
|
||||||
|
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
#include <openssl/pem.h>
|
#include <openssl/pem.h>
|
||||||
|
@ -27,6 +27,8 @@ RCSID("$OpenBSD: ssh-keygen.c,v 1.118 2004/12/23 17:38:07 markus Exp $");
|
||||||
#include "pathnames.h"
|
#include "pathnames.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
|
#include "match.h"
|
||||||
|
#include "hostfile.h"
|
||||||
|
|
||||||
#ifdef SMARTCARD
|
#ifdef SMARTCARD
|
||||||
#include "scard.h"
|
#include "scard.h"
|
||||||
|
@ -50,6 +52,13 @@ int change_comment = 0;
|
||||||
|
|
||||||
int quiet = 0;
|
int quiet = 0;
|
||||||
|
|
||||||
|
/* Flag indicating that we want to hash a known_hosts file */
|
||||||
|
int hash_hosts = 0;
|
||||||
|
/* Flag indicating that we want lookup a host in known_hosts file */
|
||||||
|
int find_host = 0;
|
||||||
|
/* Flag indicating that we want to delete a host from a known_hosts file */
|
||||||
|
int delete_host = 0;
|
||||||
|
|
||||||
/* Flag indicating that we just want to see the key fingerprint */
|
/* Flag indicating that we just want to see the key fingerprint */
|
||||||
int print_fingerprint = 0;
|
int print_fingerprint = 0;
|
||||||
int print_bubblebabble = 0;
|
int print_bubblebabble = 0;
|
||||||
|
@ -541,6 +550,194 @@ do_fingerprint(struct passwd *pw)
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_host(FILE *f, char *name, Key *public, int hash)
|
||||||
|
{
|
||||||
|
if (hash && (name = host_hash(name, NULL, 0)) == NULL)
|
||||||
|
fatal("hash_host failed");
|
||||||
|
fprintf(f, "%s ", name);
|
||||||
|
if (!key_write(public, f))
|
||||||
|
fatal("key_write failed");
|
||||||
|
fprintf(f, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
do_known_hosts(struct passwd *pw, const char *name)
|
||||||
|
{
|
||||||
|
FILE *in, *out = stdout;
|
||||||
|
Key *public;
|
||||||
|
char *cp, *cp2, *kp, *kp2;
|
||||||
|
char line[16*1024], tmp[MAXPATHLEN], old[MAXPATHLEN];
|
||||||
|
int c, i, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0;
|
||||||
|
|
||||||
|
if (!have_identity) {
|
||||||
|
cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid);
|
||||||
|
if (strlcpy(identity_file, cp, sizeof(identity_file)) >=
|
||||||
|
sizeof(identity_file))
|
||||||
|
fatal("Specified known hosts path too long");
|
||||||
|
xfree(cp);
|
||||||
|
have_identity = 1;
|
||||||
|
}
|
||||||
|
if ((in = fopen(identity_file, "r")) == NULL)
|
||||||
|
fatal("fopen: %s", strerror(errno));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find hosts goes to stdout, hash and deletions happen in-place
|
||||||
|
* A corner case is ssh-keygen -HF foo, which should go to stdout
|
||||||
|
*/
|
||||||
|
if (!find_host && (hash_hosts || delete_host)) {
|
||||||
|
if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) ||
|
||||||
|
strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) ||
|
||||||
|
strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) ||
|
||||||
|
strlcat(old, ".old", sizeof(old)) >= sizeof(old))
|
||||||
|
fatal("known_hosts path too long");
|
||||||
|
umask(077);
|
||||||
|
if ((c = mkstemp(tmp)) == -1)
|
||||||
|
fatal("mkstemp: %s", strerror(errno));
|
||||||
|
if ((out = fdopen(c, "w")) == NULL) {
|
||||||
|
c = errno;
|
||||||
|
unlink(tmp);
|
||||||
|
fatal("fdopen: %s", strerror(c));
|
||||||
|
}
|
||||||
|
inplace = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), in)) {
|
||||||
|
num++;
|
||||||
|
i = strlen(line) - 1;
|
||||||
|
if (line[i] != '\n') {
|
||||||
|
error("line %d too long: %.40s...", num, line);
|
||||||
|
skip = 1;
|
||||||
|
invalid = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (skip) {
|
||||||
|
skip = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
line[i] = '\0';
|
||||||
|
|
||||||
|
/* Skip leading whitespace, empty and comment lines. */
|
||||||
|
for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
|
||||||
|
;
|
||||||
|
if (!*cp || *cp == '\n' || *cp == '#') {
|
||||||
|
if (inplace)
|
||||||
|
fprintf(out, "%s\n", cp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Find the end of the host name portion. */
|
||||||
|
for (kp = cp; *kp && *kp != ' ' && *kp != '\t'; kp++)
|
||||||
|
;
|
||||||
|
if (*kp == '\0' || *(kp + 1) == '\0') {
|
||||||
|
error("line %d missing key: %.40s...",
|
||||||
|
num, line);
|
||||||
|
invalid = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*kp++ = '\0';
|
||||||
|
kp2 = kp;
|
||||||
|
|
||||||
|
public = key_new(KEY_RSA1);
|
||||||
|
if (key_read(public, &kp) != 1) {
|
||||||
|
kp = kp2;
|
||||||
|
key_free(public);
|
||||||
|
public = key_new(KEY_UNSPEC);
|
||||||
|
if (key_read(public, &kp) != 1) {
|
||||||
|
error("line %d invalid key: %.40s...",
|
||||||
|
num, line);
|
||||||
|
key_free(public);
|
||||||
|
invalid = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*cp == HASH_DELIM) {
|
||||||
|
if (find_host || delete_host) {
|
||||||
|
cp2 = host_hash(name, cp, strlen(cp));
|
||||||
|
if (cp2 == NULL) {
|
||||||
|
error("line %d: invalid hashed "
|
||||||
|
"name: %.64s...", num, line);
|
||||||
|
invalid = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
c = (strcmp(cp2, cp) == 0);
|
||||||
|
if (find_host && c) {
|
||||||
|
printf("# Host %s found: "
|
||||||
|
"line %d type %s\n", name,
|
||||||
|
num, key_type(public));
|
||||||
|
print_host(out, cp, public, 0);
|
||||||
|
}
|
||||||
|
if (delete_host && !c)
|
||||||
|
print_host(out, cp, public, 0);
|
||||||
|
} else if (hash_hosts)
|
||||||
|
print_host(out, cp, public, 0);
|
||||||
|
} else {
|
||||||
|
if (find_host || delete_host) {
|
||||||
|
c = (match_hostname(name, cp,
|
||||||
|
strlen(cp)) == 1);
|
||||||
|
if (find_host && c) {
|
||||||
|
printf("# Host %s found: "
|
||||||
|
"line %d type %s\n", name,
|
||||||
|
num, key_type(public));
|
||||||
|
print_host(out, cp, public, hash_hosts);
|
||||||
|
}
|
||||||
|
if (delete_host && !c)
|
||||||
|
print_host(out, cp, public, 0);
|
||||||
|
} else if (hash_hosts) {
|
||||||
|
for(cp2 = strsep(&cp, ",");
|
||||||
|
cp2 != NULL && *cp2 != '\0';
|
||||||
|
cp2 = strsep(&cp, ","))
|
||||||
|
print_host(out, cp2, public, 1);
|
||||||
|
has_unhashed = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
key_free(public);
|
||||||
|
}
|
||||||
|
fclose(in);
|
||||||
|
|
||||||
|
if (invalid) {
|
||||||
|
fprintf(stderr, "%s is not a valid known_host file.\n",
|
||||||
|
identity_file);
|
||||||
|
if (inplace) {
|
||||||
|
fprintf(stderr, "Not replacing existing known_hosts "
|
||||||
|
"file beacuse of errors");
|
||||||
|
fclose(out);
|
||||||
|
unlink(tmp);
|
||||||
|
}
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inplace) {
|
||||||
|
fclose(out);
|
||||||
|
|
||||||
|
/* Backup existing file */
|
||||||
|
if (unlink(old) == -1 && errno != ENOENT)
|
||||||
|
fatal("unlink %.100s: %s", old, strerror(errno));
|
||||||
|
if (link(identity_file, old) == -1)
|
||||||
|
fatal("link %.100s to %.100s: %s", identity_file, old,
|
||||||
|
strerror(errno));
|
||||||
|
/* Move new one into place */
|
||||||
|
if (rename(tmp, identity_file) == -1) {
|
||||||
|
error("rename\"%s\" to \"%s\": %s", tmp, identity_file,
|
||||||
|
strerror(errno));
|
||||||
|
unlink(tmp);
|
||||||
|
unlink(old);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "%s updated.\n", identity_file);
|
||||||
|
fprintf(stderr, "Original contents retained as %s\n", old);
|
||||||
|
if (has_unhashed) {
|
||||||
|
fprintf(stderr, "WARNING: %s contains unhashed "
|
||||||
|
"entries\n", old);
|
||||||
|
fprintf(stderr, "Delete this file to ensure privacy "
|
||||||
|
"of hostnames\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Perform changing a passphrase. The argument is the passwd structure
|
* Perform changing a passphrase. The argument is the passwd structure
|
||||||
* for the current user.
|
* for the current user.
|
||||||
|
@ -767,6 +964,8 @@ usage(void)
|
||||||
fprintf(stderr, " -y Read private key file and print public key.\n");
|
fprintf(stderr, " -y Read private key file and print public key.\n");
|
||||||
fprintf(stderr, " -t type Specify type of key to create.\n");
|
fprintf(stderr, " -t type Specify type of key to create.\n");
|
||||||
fprintf(stderr, " -B Show bubblebabble digest of key file.\n");
|
fprintf(stderr, " -B Show bubblebabble digest of key file.\n");
|
||||||
|
fprintf(stderr, " -H Hash names in known_hosts file\n");
|
||||||
|
fprintf(stderr, " -F hostname Find hostname in known hosts file\n");
|
||||||
fprintf(stderr, " -C comment Provide new comment.\n");
|
fprintf(stderr, " -C comment Provide new comment.\n");
|
||||||
fprintf(stderr, " -N phrase Provide new passphrase.\n");
|
fprintf(stderr, " -N phrase Provide new passphrase.\n");
|
||||||
fprintf(stderr, " -P phrase Provide old passphrase.\n");
|
fprintf(stderr, " -P phrase Provide old passphrase.\n");
|
||||||
|
@ -790,7 +989,7 @@ main(int ac, char **av)
|
||||||
{
|
{
|
||||||
char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2;
|
char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2;
|
||||||
char out_file[MAXPATHLEN], *reader_id = NULL;
|
char out_file[MAXPATHLEN], *reader_id = NULL;
|
||||||
char *resource_record_hostname = NULL;
|
char *rr_hostname = NULL;
|
||||||
Key *private, *public;
|
Key *private, *public;
|
||||||
struct passwd *pw;
|
struct passwd *pw;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
@ -824,7 +1023,7 @@ main(int ac, char **av)
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((opt = getopt(ac, av,
|
while ((opt = getopt(ac, av,
|
||||||
"degiqpclBRvxXyb:f:t:U:D:P:N:C:r:g:T:G:M:S:a:W:")) != -1) {
|
"degiqpclBHvxXyF:b:f:t:U:D:P:N:C:r:g:R:T:G:M:S:a:W:")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'b':
|
case 'b':
|
||||||
bits = atoi(optarg);
|
bits = atoi(optarg);
|
||||||
|
@ -833,6 +1032,17 @@ main(int ac, char **av)
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'F':
|
||||||
|
find_host = 1;
|
||||||
|
rr_hostname = optarg;
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
hash_hosts = 1;
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
delete_host = 1;
|
||||||
|
rr_hostname = optarg;
|
||||||
|
break;
|
||||||
case 'l':
|
case 'l':
|
||||||
print_fingerprint = 1;
|
print_fingerprint = 1;
|
||||||
break;
|
break;
|
||||||
|
@ -864,10 +1074,6 @@ main(int ac, char **av)
|
||||||
case 'q':
|
case 'q':
|
||||||
quiet = 1;
|
quiet = 1;
|
||||||
break;
|
break;
|
||||||
case 'R':
|
|
||||||
/* unused */
|
|
||||||
exit(0);
|
|
||||||
break;
|
|
||||||
case 'e':
|
case 'e':
|
||||||
case 'x':
|
case 'x':
|
||||||
/* export key */
|
/* export key */
|
||||||
|
@ -902,7 +1108,7 @@ main(int ac, char **av)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
resource_record_hostname = optarg;
|
rr_hostname = optarg;
|
||||||
break;
|
break;
|
||||||
case 'W':
|
case 'W':
|
||||||
generator_wanted = atoi(optarg);
|
generator_wanted = atoi(optarg);
|
||||||
|
@ -945,6 +1151,8 @@ main(int ac, char **av)
|
||||||
printf("Can only have one of -p and -c.\n");
|
printf("Can only have one of -p and -c.\n");
|
||||||
usage();
|
usage();
|
||||||
}
|
}
|
||||||
|
if (delete_host || hash_hosts || find_host)
|
||||||
|
do_known_hosts(pw, rr_hostname);
|
||||||
if (print_fingerprint || print_bubblebabble)
|
if (print_fingerprint || print_bubblebabble)
|
||||||
do_fingerprint(pw);
|
do_fingerprint(pw);
|
||||||
if (change_passphrase)
|
if (change_passphrase)
|
||||||
|
@ -957,8 +1165,8 @@ main(int ac, char **av)
|
||||||
do_convert_from_ssh2(pw);
|
do_convert_from_ssh2(pw);
|
||||||
if (print_public)
|
if (print_public)
|
||||||
do_print_public(pw);
|
do_print_public(pw);
|
||||||
if (resource_record_hostname != NULL) {
|
if (rr_hostname != NULL) {
|
||||||
do_print_resource_record(pw, resource_record_hostname);
|
do_print_resource_record(pw, rr_hostname);
|
||||||
}
|
}
|
||||||
if (reader_id != NULL) {
|
if (reader_id != NULL) {
|
||||||
#ifdef SMARTCARD
|
#ifdef SMARTCARD
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
.\"
|
.\"
|
||||||
.\" $OpenBSD: ssh_config.5,v 1.44 2005/03/01 10:40:27 djm Exp $
|
.\" $OpenBSD: ssh_config.5,v 1.45 2005/03/01 10:42:49 djm Exp $
|
||||||
.Dd September 25, 1999
|
.Dd September 25, 1999
|
||||||
.Dt SSH_CONFIG 5
|
.Dt SSH_CONFIG 5
|
||||||
.Os
|
.Os
|
||||||
|
@ -421,7 +421,8 @@ be disclosed.
|
||||||
The default is
|
The default is
|
||||||
.Dq no .
|
.Dq no .
|
||||||
Note that hashing of names and addresses will not be retrospectively applied
|
Note that hashing of names and addresses will not be retrospectively applied
|
||||||
to existing known hosts files.
|
to existing known hosts files, but these may be manually hashed using
|
||||||
|
.Xr ssh-keygen 1 .
|
||||||
.It Cm HostbasedAuthentication
|
.It Cm HostbasedAuthentication
|
||||||
Specifies whether to try rhosts based authentication with public key
|
Specifies whether to try rhosts based authentication with public key
|
||||||
authentication.
|
authentication.
|
||||||
|
|
Loading…
Reference in New Issue