From cddcfecfd1a8f91ee1da1c0147a4f07aee1846a0 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Mon, 29 Jul 1996 00:18:42 +0000 Subject: Sat Jul 27 13:02:09 1996 Ulrich Drepper * elf/rtld.c (dl_main): Ignore arguments when environment variable LD_TRACE_LOADED_OBJECTS is set. Sun Jul 28 19:42:51 1996 Roland McGrath * sysdeps/generic/dl-sysdep.c (_dl_sysdep_start): Initialize __libc_enable_secure instead of _dl_secure. * sysdeps/mach/hurd/dl-sysdep.c (_dl_sysdep_start): Likewise. * elf/dl-load.c (_dl_map_object): Check __libc_enable_secure instead of _dl_secure. * elf/Makefile (routines): Add enbl-secure. (elide-routines.so): Here too. * sysdeps/generic/enbl-secure.c: New file. * posix/unistd.h [__USE_GNU]: Declare `__libc_enable_secure' flag. * elf/dl-support.c (_dl_secure): Variable removed. * elf/rtld.c: Likewise. Replace all uses with __libc_enable_secure. Sun Jul 28 19:26:40 1996 David S. Miller * sysdeps/mips/dl-machine.h (ELF_MACHINE_RUNTIME_TRAMPOLINE): Declare _dl_runtime_resolve with __attribute__ ((unused)) so the compiler doesn't elide it. (elf_machine_rel): Follow Jul 14 change in sysdeps/i386/dl-machine.h. --- ChangeLog | 26 ++++ db/makedb.c | 325 ++++++++++++++++++++++++++++++++++++++++++ elf/Makefile | 5 +- elf/dl-load.c | 2 +- elf/dl-support.c | 3 - elf/rtld.c | 49 +++---- posix/unistd.h | 8 ++ sunrpc/rpc/auth.h | 5 - sunrpc/rpc/auth_unix.h | 6 - sunrpc/rpc/pmap_clnt.h | 5 - sunrpc/rpc/pmap_rmt.h | 5 +- sunrpc/rpc/rpc_msg.h | 11 +- sysdeps/generic/dl-sysdep.c | 2 +- sysdeps/generic/enbl-secure.c | 34 +++++ sysdeps/mach/hurd/dl-sysdep.c | 2 +- 15 files changed, 424 insertions(+), 64 deletions(-) create mode 100644 db/makedb.c create mode 100644 sysdeps/generic/enbl-secure.c diff --git a/ChangeLog b/ChangeLog index ee3ebc5dc9..2c67cffa29 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,29 @@ +Sat Jul 27 13:02:09 1996 Ulrich Drepper + + * elf/rtld.c (dl_main): Ignore arguments when environment variable + LD_TRACE_LOADED_OBJECTS is set. + +Sun Jul 28 19:42:51 1996 Roland McGrath + + * sysdeps/generic/dl-sysdep.c (_dl_sysdep_start): Initialize + __libc_enable_secure instead of _dl_secure. + * sysdeps/mach/hurd/dl-sysdep.c (_dl_sysdep_start): Likewise. + * elf/dl-load.c (_dl_map_object): Check __libc_enable_secure instead + of _dl_secure. + * elf/Makefile (routines): Add enbl-secure. + (elide-routines.so): Here too. + * sysdeps/generic/enbl-secure.c: New file. + * posix/unistd.h [__USE_GNU]: Declare `__libc_enable_secure' flag. + * elf/dl-support.c (_dl_secure): Variable removed. + * elf/rtld.c: Likewise. Replace all uses with __libc_enable_secure. + +Sun Jul 28 19:26:40 1996 David S. Miller + + * sysdeps/mips/dl-machine.h (ELF_MACHINE_RUNTIME_TRAMPOLINE): + Declare _dl_runtime_resolve with __attribute__ ((unused)) so the + compiler doesn't elide it. + (elf_machine_rel): Follow Jul 14 change in sysdeps/i386/dl-machine.h. + Sat Jul 27 02:58:49 1996 Roland McGrath * elf/rtld.c (dl_main): Take new option --verify when run directly. diff --git a/db/makedb.c b/db/makedb.c new file mode 100644 index 0000000000..b1ed982275 --- /dev/null +++ b/db/makedb.c @@ -0,0 +1,325 @@ +/* makedb -- create simple DB database from textual input. +Copyright (C) 1996 Free Software Foundation, Inc. +This file is part of the GNU C Library. +Contributed by Ulrich Drepper , 1996. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Get libc version number. */ +#include "../version.h" + + +/* Long options. */ +static const struct option long_options[] = +{ + { "help", no_argument, NULL, 'h' }, + { "fold-case", no_argument, NULL, 'f' }, + { "output", required_argument, NULL, 'o' }, + { "undo", no_argument, NULL, 'u' }, + { "version", no_argument, NULL, 'V' }, + { NULL, } +}; + +/* Prototypes for local functions. */ +static void usage __P ((int status)) __attribute__ ((noreturn)); +static int process_input __P ((FILE *input, const char *inname, DB *output, + int to_lowercase)); +static int print_database __P ((DB *db)); + + +int +main (argc, argv) + int argc; + char *argv[]; +{ + const char *output_name; + const char *input_name; + FILE *input_file; + DB *db_file; + int do_help; + int do_version; + int to_lowercase; + int do_undo; + int status; + int opt; + + /* Set locale via LC_ALL. */ + setlocale (LC_ALL, ""); + + /* Set the text message domain. */ + textdomain (_libc_intl_domainname); + + /* Initialize local variables. */ + do_help = 0; + do_version = 0; + to_lowercase = 0; + do_undo = 0; + output_name = NULL; + + while ((opt = getopt_long (argc, argv, "fho:uV", long_options, NULL)) != EOF) + switch (opt) + { + case '\0': /* Long option. */ + break; + case 'h': + do_help = 1; + break; + case 'f': + to_lowercase = 1; + break; + case 'o': + output_name = optarg; + break; + case 'u': + do_undo = 1; + break; + case 'V': + do_version = 1; + break; + default: + usage (EXIT_FAILURE); + } + + /* Version information is requested. */ + if (do_version) + printf ("%s - GNU %s %s\n", program_invocation_name, "libc", VERSION); + + /* Help is requested. */ + if (do_help) + usage (EXIT_SUCCESS); + else if (do_version) + exit (EXIT_SUCCESS); + + /* Determine file names. */ + if (do_undo || output_name != NULL) + { + if (optind + 1 != argc) + { + wrong_arguments: + error (0, 0, gettext ("wrong number of arguments")); + usage (EXIT_FAILURE); + } + input_name = argv[optind]; + } + else + { + if (optind + 2 != argc) + goto wrong_arguments; + + input_name = argv[optind++]; + output_name = argv[optind]; + } + + /* Special handling if we are asked to print the database. */ + if (do_undo) + { + db_file = dbopen (input_name, O_RDONLY, 0666, DB_BTREE, NULL); + if (db_file == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot open database file `%s': %s"), + input_name, + errno == EFTYPE ? gettext ("incorrectly formatted file") + : strerror (errno)); + + status = print_database (db_file); + + db_file->close (db_file); + + return status; + } + + /* Open input file. */ + if (strcmp (input_name, "-") == 0 || strcmp (input_name, "/dev/stdin") == 0) + input_file = stdin; + else + { + input_file = fopen (input_name, "r"); + if (input_file == NULL) + error (EXIT_FAILURE, errno, gettext ("cannot open input file `%s'"), + input_name); + } + + /* Open output file. This must not be standard output so we don't + handle "-" and "/dev/stdout" special. */ + db_file = dbopen (output_name, O_CREAT | O_RDWR | O_TRUNC, 0666, + DB_BTREE, NULL); + if (db_file == NULL) + error (EXIT_FAILURE, errno, gettext ("cannot open output file `%s'")); + + /* Start the real work. */ + status = process_input (input_file, input_name, db_file, to_lowercase); + + /* Close files. */ + if (input_file != stdin) + fclose (input_file); + db_file->close (db_file); + + return status; +} + + +static void +usage (status) + int status; +{ + if (status != EXIT_SUCCESS) + fprintf (stderr, gettext ("Try `%s --help' for more information.\n"), + program_invocation_name); + else + printf (gettext ("\ +Usage: %s [OPTION]... INPUT-FILE OUTPUT-FILE\n\ + %s [OPTION]... -o OUTPUT-FILE INPUT-FILE\n\ + %s [OPTION]... -u INPUT-FILE\n\ +Mandatory arguments to long options are mandatory for short options too.\n\ + -f, --fold-case convert key to lower case\n\ + -h, --help display this help and exit\n\ + -o, --output=NAME write output to file NAME\n\ + -u, --undo print content of database file, one entry a line\n\ + -V, --version output version information and exit\n\ +If INPUT-FILE is -, input is read from standard input.\n"), + program_invocation_name, program_invocation_name, + program_invocation_name); + + exit (status); +} + + +static int +process_input (input, inname, output, to_lowercase) + FILE *input; + const char *inname; + DB *output; + int to_lowercase; +{ + char *line; + size_t linelen; + int status; + size_t linenr; + + line = NULL; + linelen = 0; + status = EXIT_SUCCESS; + linenr = 0; + + while (!feof (input)) + { + DBT key; + DBT val; + char *cp; + int n; + + n = getline (&line, &linelen, input); + if (n < 0) + /* This means end of file or some bug. */ + break; + if (n == 0) + /* Short read. Probably interrupted system call. */ + continue; + + ++linenr; + + if (line[n - 1] == '\n') + /* Remove trailing newline. */ + line[--n] = '\0'; + + cp = line; + while (isspace (*cp)) + ++cp; + + if (*cp == '#') + /* First non-space character in line '#': it's a comment. */ + continue; + + key.data = cp; + while (*cp != '\0' && !isspace (*cp)) + { + if (to_lowercase) + *cp = tolower (*cp); + ++cp; + } + + if (key.data == cp) + /* It's an empty line. */ + continue; + + key.size = cp - (char *) key.data; + + while (isspace (*cp)) + ++cp; + + val.data = cp; + val.size = &line[n] - cp; + + /* Store the value. */ + status = output->put (output, &key, &val, R_NOOVERWRITE); + if (status != 0) + { + if (status == 1) + error_at_line (0, 0, inname, linenr, gettext ("duplicate key")); + else + error (0, errno, gettext ("while writing data base file")); + + status = EXIT_FAILURE; + clearerr (input); + break; + } + } + + if (ferror (input)) + { + error (0, 0, gettext ("problems while reading `%s'")); + status = EXIT_FAILURE; + } + + return status; +} + + +static int +print_database (db) + DB *db; +{ + DBT key; + DBT val; + int no_more; + + no_more = db->seq (db, &key, &val, R_FIRST); + while (!no_more) + { + printf ("%.*s %.*s\n", (int) key.size, (char *) key.data, (int) val.size, + (char *) val.data); + + no_more = db->seq (db, &key, &val, R_NEXT); + } + + if (no_more == -1) + { + error (0, errno, gettext ("while reading database")); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/elf/Makefile b/elf/Makefile index 44c510ac43..a19a8b42ba 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -21,14 +21,15 @@ subdir := elf headers = elf.h elfclass.h link.h dlfcn.h -routines = $(dl-routines) dl-open dl-close dl-symbol dl-support +routines = $(dl-routines) dl-open dl-close dl-symbol dl-support \ + enbl-secure # The core dynamic linking functions are in libc for the static and # profiled libraries. dl-routines = $(addprefix dl-,load cache lookup object reloc deps \ runtime error init fini debug) # But they are absent from the shared libc, because that code is in ld.so. -elide-routines.so = $(dl-routines) dl-support +elide-routines.so = $(dl-routines) dl-support enbl-secure # ld.so uses those routines, plus some special stuff for being the program # interpreter and operating independent of libc. diff --git a/elf/dl-load.c b/elf/dl-load.c index fc2733252a..8ccc838b2b 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -511,7 +511,7 @@ _dl_map_object (struct link_map *loader, const char *name, int type) l->l_info[DT_STRTAB]->d_un.d_ptr + l->l_info[DT_RPATH]->d_un.d_val)); /* Try an environment variable (unless setuid). */ - if (fd == -1 && ! _dl_secure) + if (fd == -1 && ! __libc_enable_secure) trypath (getenv ("LD_LIBRARY_PATH")); if (fd == -1) { diff --git a/elf/dl-support.c b/elf/dl-support.c index b85d470770..50ebfbfe5a 100644 --- a/elf/dl-support.c +++ b/elf/dl-support.c @@ -24,8 +24,6 @@ Cambridge, MA 02139, USA. */ /* This file defines some things that for the dynamic linker are defined in rtld.c and dl-sysdep.c in ways appropriate to bootstrap dynamic linking. */ -int _dl_secure; /* Always honor LD_LIBRARY_PATH. */ - extern char *__progname; char **_dl_argv = &__progname; /* This is checked for some error messages. */ @@ -48,4 +46,3 @@ _dl_sysdep_fatal (void) { assert (! "_dl_sysdep_fatal called"); } - diff --git a/elf/rtld.c b/elf/rtld.c index 35a94b24cc..3657efdc61 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -30,7 +30,7 @@ Cambridge, MA 02139, USA. */ /* System-specific function to do initial startup for the dynamic linker. After this, file access calls and getenv must work. This is responsible - for setting _dl_secure if we need to be secure (e.g. setuid), + for setting __libc_enable_secure if we need to be secure (e.g. setuid), and for setting _dl_argc and _dl_argv, and then calling _dl_main. */ extern ElfW(Addr) _dl_sysdep_start (void **start_argptr, void (*dl_main) (const ElfW(Phdr) *phdr, @@ -38,7 +38,6 @@ extern ElfW(Addr) _dl_sysdep_start (void **start_argptr, ElfW(Addr) *user_entry)); extern void _dl_sysdep_start_cleanup (void); -int _dl_secure; int _dl_argc; char **_dl_argv; const char *_dl_rpath; @@ -269,7 +268,7 @@ of this helper program; chances are you did not intend to run this program.\n", preloads = NULL; npreloads = 0; - if (! _dl_secure) + if (! __libc_enable_secure) { const char *preloadlist = getenv ("LD_PRELOAD"); if (preloadlist) @@ -340,10 +339,7 @@ of this helper program; chances are you did not intend to run this program.\n", } } - if (mode == normal && getenv ("LD_TRACE_LOADED_OBJECTS") != NULL) - mode = list; - - if (mode != normal) + if (mode != normal || getenv ("LD_TRACE_LOADED_OBJECTS") != NULL) { /* We were run just to list the shared libraries. It is important that we do this before real relocation, because the @@ -366,29 +362,30 @@ of this helper program; chances are you did not intend to run this program.\n", " (0x", bp, ")\n", NULL); } - for (i = 1; i < _dl_argc; ++i) - { - const ElfW(Sym) *ref = NULL; - ElfW(Addr) loadbase = _dl_lookup_symbol (_dl_argv[i], &ref, - &_dl_default_scope[2], - "argument", 0, 0); - char buf[20], *bp; - buf[sizeof buf - 1] = '\0'; - bp = _itoa (ref->st_value, &buf[sizeof buf - 1], 16, 0); - while (&buf[sizeof buf - 1] - bp < sizeof loadbase * 2) - *--bp = '0'; - _dl_sysdep_message (_dl_argv[i], " found at 0x", bp, NULL); - buf[sizeof buf - 1] = '\0'; - bp = _itoa (loadbase, &buf[sizeof buf - 1], 16, 0); - while (&buf[sizeof buf - 1] - bp < sizeof loadbase * 2) - *--bp = '0'; - _dl_sysdep_message (" in object at 0x", bp, "\n", NULL); - } + if (mode != normal) + for (i = 1; i < _dl_argc; ++i) + { + const ElfW(Sym) *ref = NULL; + ElfW(Addr) loadbase = _dl_lookup_symbol (_dl_argv[i], &ref, + &_dl_default_scope[2], + "argument", 0, 0); + char buf[20], *bp; + buf[sizeof buf - 1] = '\0'; + bp = _itoa (ref->st_value, &buf[sizeof buf - 1], 16, 0); + while (&buf[sizeof buf - 1] - bp < sizeof loadbase * 2) + *--bp = '0'; + _dl_sysdep_message (_dl_argv[i], " found at 0x", bp, NULL); + buf[sizeof buf - 1] = '\0'; + bp = _itoa (loadbase, &buf[sizeof buf - 1], 16, 0); + while (&buf[sizeof buf - 1] - bp < sizeof loadbase * 2) + *--bp = '0'; + _dl_sysdep_message (" in object at 0x", bp, "\n", NULL); + } _exit (0); } - lazy = !_dl_secure && *(getenv ("LD_BIND_NOW") ?: "") == '\0'; + lazy = !__libc_enable_secure && *(getenv ("LD_BIND_NOW") ?: "") == '\0'; { /* Now we have all the objects loaded. Relocate them all except for diff --git a/posix/unistd.h b/posix/unistd.h index 1247728121..424cedd2ec 100644 --- a/posix/unistd.h +++ b/posix/unistd.h @@ -776,6 +776,14 @@ extern int lockf __P ((int __fd, int __cmd, __off_t __len)); while (__result == -1L && errno == EINTR); \ __result; })) \ + +/* This variable is set nonzero at startup if the process's effective IDs + differ from its real IDs, or it is otherwise indicated that extra + security should be used. When this is set the dynamic linker ignores + the various environment variables that normally affect it. */ + +extern int __libc_enable_secure; + #endif #ifdef __USE_POSIX diff --git a/sunrpc/rpc/auth.h b/sunrpc/rpc/auth.h index 0e7e2a1e9e..a40bddd4c3 100644 --- a/sunrpc/rpc/auth.h +++ b/sunrpc/rpc/auth.h @@ -87,11 +87,6 @@ struct opaque_auth { u_int oa_length; /* not to exceed MAX_AUTH_BYTES */ }; -#ifndef _RPC_AUTH_H - -#define _RPC_AUTH_H 1 -#include - /* * Auth handle, interface to client side authenticators. */ diff --git a/sunrpc/rpc/auth_unix.h b/sunrpc/rpc/auth_unix.h index 19b891cc29..3fb711894e 100644 --- a/sunrpc/rpc/auth_unix.h +++ b/sunrpc/rpc/auth_unix.h @@ -49,12 +49,6 @@ __BEGIN_DECLS -#ifndef _RPC_AUTH_UNIX_H - -#define _RPC_AUTH_UNIX_H 1 -#include - - /* The machine name is part of a credential; it may not exceed 255 bytes */ #define MAX_MACHINE_NAME 255 diff --git a/sunrpc/rpc/pmap_clnt.h b/sunrpc/rpc/pmap_clnt.h index 95f0749f87..3acfa5974d 100644 --- a/sunrpc/rpc/pmap_clnt.h +++ b/sunrpc/rpc/pmap_clnt.h @@ -40,11 +40,6 @@ * Copyright (C) 1984, Sun Microsystems, Inc. */ -#ifndef _RPC_PMAP_CLNT_H - -#define _RPC_PMAP_CLNT_H 1 -#include - __BEGIN_DECLS typedef bool_t (*resultproc_t)(); diff --git a/sunrpc/rpc/pmap_rmt.h b/sunrpc/rpc/pmap_rmt.h index 15822ef642..f5d4af110b 100644 --- a/sunrpc/rpc/pmap_rmt.h +++ b/sunrpc/rpc/pmap_rmt.h @@ -47,10 +47,7 @@ struct rmtcallargs { xdrproc_t xdr_args; }; -bool_t xdr_rmtcallres __P ((XDR *__xdrs, struct rmtcallres *__crp)); - - -#endif /* rpc/pmap_rmt.h */ +bool_t xdr_rmtcall_args __P ((XDR *__xdrs, struct rmtcallargs *__crp)); struct rmtcallres { u_long *port_ptr; diff --git a/sunrpc/rpc/rpc_msg.h b/sunrpc/rpc/rpc_msg.h index 533ec92068..9c37a4e3f9 100644 --- a/sunrpc/rpc/rpc_msg.h +++ b/sunrpc/rpc/rpc_msg.h @@ -42,12 +42,6 @@ #include -#ifndef _RPC_RPC_MSG_H - -#define _RPC_RPC_MSG_H 1 -#include - - #define RPC_MSG_VERSION ((u_long) 2) #define RPC_SERVICE_PORT ((u_short) 2048) @@ -55,7 +49,7 @@ __BEGIN_DECLS /* * Bottom up definition of an rpc message. - * NOTE: call and reply use the same overall stuct but + * NOTE: call and reply use the same overall struct but * different parts of unions within it. */ @@ -201,9 +195,6 @@ extern bool_t xdr_replymsg __P ((XDR *__xdrs, struct rpc_msg *__rmsg)); extern void _seterr_reply __P ((struct rpc_msg *__msg, struct rpc_err *__error)); -#endif /* rpc/rpc_msg.h */ - struct rpc_err *__error)); - __END_DECLS #endif /* rpc/rpc_msg.h */ diff --git a/sysdeps/generic/dl-sysdep.c b/sysdeps/generic/dl-sysdep.c index 87eac71687..a4b8639bc4 100644 --- a/sysdeps/generic/dl-sysdep.c +++ b/sysdeps/generic/dl-sysdep.c @@ -96,7 +96,7 @@ _dl_sysdep_start (void **start_argptr, SEE (EGID, egid); - _dl_secure = uid != euid || gid != egid; + __libc_enable_secure = uid != euid || gid != egid; #ifdef DL_SYSDEP_INIT DL_SYSDEP_INIT; diff --git a/sysdeps/generic/enbl-secure.c b/sysdeps/generic/enbl-secure.c new file mode 100644 index 0000000000..b7530a8437 --- /dev/null +++ b/sysdeps/generic/enbl-secure.c @@ -0,0 +1,34 @@ +/* Define and initialize the `__libc_enable_secure' flag. Generic version. +Copyright (C) 1996 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +/* This file is used in the static libc. For the shared library, + dl-sysdep.c defines and initializes __libc_enable_secure. */ + +#include + + +/* Safest assumption, if somehow the initializer isn't run. */ +int __libc_enable_secure = 1; + +static void __attribute__ ((unused, constructor)) +init_secure (void) +{ + __libc_enable_secure = (__geteuid () != __getuid () || + __getegid () != __getgid ()); +} diff --git a/sysdeps/mach/hurd/dl-sysdep.c b/sysdeps/mach/hurd/dl-sysdep.c index ee2bf73469..be0417ca30 100644 --- a/sysdeps/mach/hurd/dl-sysdep.c +++ b/sysdeps/mach/hurd/dl-sysdep.c @@ -107,7 +107,7 @@ _dl_sysdep_start (void **start_argptr, else _dl_hurd_data = (void *) p; - _dl_secure = _dl_hurd_data->flags & EXEC_SECURE; + __libc_enable_secure = _dl_hurd_data->flags & EXEC_SECURE; if (_dl_hurd_data->flags & EXEC_STACK_ARGS && _dl_hurd_data->user_entry == 0) -- cgit v1.2.3