int cmd_receive_pack(int argc, const char **argv, const char *prefix) { int advertise_refs = 0; struct command *commands; struct sha1_array shallow = SHA1_ARRAY_INIT; struct sha1_array ref = SHA1_ARRAY_INIT; struct shallow_info si; struct option options[] = { OPT__QUIET(&quiet, N_("quiet")), OPT_HIDDEN_BOOL(0, "stateless-rpc", &stateless_rpc, NULL), OPT_HIDDEN_BOOL(0, "advertise-refs", &advertise_refs, NULL), OPT_HIDDEN_BOOL(0, "reject-thin-pack-for-testing", &reject_thin, NULL), OPT_END() }; packet_trace_identity("receive-pack"); argc = parse_options(argc, argv, prefix, options, receive_pack_usage, 0); if (argc > 1) usage_msg_opt(_("Too many arguments."), receive_pack_usage, options); if (argc == 0) usage_msg_opt(_("You must specify a directory."), receive_pack_usage, options); service_dir = argv[0]; setup_path(); if (!enter_repo(service_dir, 0)) die("'%s' does not appear to be a git repository", service_dir); git_config(receive_pack_config, NULL); if (cert_nonce_seed) push_cert_nonce = prepare_push_cert_nonce(service_dir, time(NULL)); if (0 <= transfer_unpack_limit) unpack_limit = transfer_unpack_limit; else if (0 <= receive_unpack_limit) unpack_limit = receive_unpack_limit; if (advertise_refs || !stateless_rpc) { write_head_info(); } if (advertise_refs) return 0; if ((commands = read_head_info(&shallow)) != NULL) { const char *unpack_status = NULL; struct string_list push_options = STRING_LIST_INIT_DUP; if (use_push_options) read_push_options(&push_options); prepare_shallow_info(&si, &shallow); if (!si.nr_ours && !si.nr_theirs) shallow_update = 0; if (!delete_only(commands)) { unpack_status = unpack_with_sideband(&si); update_shallow_info(commands, &si, &ref); } use_keepalive = KEEPALIVE_ALWAYS; execute_commands(commands, unpack_status, &si, &push_options); if (pack_lockfile) unlink_or_warn(pack_lockfile); if (report_status) report(commands, unpack_status); run_receive_hook(commands, "post-receive", 1, &push_options); run_update_post_hook(commands); if (push_options.nr) string_list_clear(&push_options, 0); if (auto_gc) { const char *argv_gc_auto[] = { "gc", "--auto", "--quiet", NULL, }; struct child_process proc = CHILD_PROCESS_INIT; proc.no_stdin = 1; proc.stdout_to_stderr = 1; proc.err = use_sideband ? -1 : 0; proc.git_cmd = 1; proc.argv = argv_gc_auto; close_all_packs(); if (!start_command(&proc)) { if (use_sideband) copy_to_sideband(proc.err, -1, NULL); finish_command(&proc); } } if (auto_update_server_info) update_server_info(0); clear_shallow_info(&si); } if (use_sideband) packet_flush(1); sha1_array_clear(&shallow); sha1_array_clear(&ref); free((void *)push_cert_nonce); return 0; }
static const char *check_nonce(const char *buf, size_t len) { char *nonce = find_header(buf, len, "nonce"); unsigned long stamp, ostamp; char *bohmac, *expect = NULL; const char *retval = NONCE_BAD; if (!nonce) { retval = NONCE_MISSING; goto leave; } else if (!push_cert_nonce) { retval = NONCE_UNSOLICITED; goto leave; } else if (!strcmp(push_cert_nonce, nonce)) { retval = NONCE_OK; goto leave; } if (!stateless_rpc) { /* returned nonce MUST match what we gave out earlier */ retval = NONCE_BAD; goto leave; } /* * In stateless mode, we may be receiving a nonce issued by * another instance of the server that serving the same * repository, and the timestamps may not match, but the * nonce-seed and dir should match, so we can recompute and * report the time slop. * * In addition, when a nonce issued by another instance has * timestamp within receive.certnonceslop seconds, we pretend * as if we issued that nonce when reporting to the hook. */ /* nonce is concat(<seconds-since-epoch>, "-", <hmac>) */ if (*nonce <= '0' || '9' < *nonce) { retval = NONCE_BAD; goto leave; } stamp = strtoul(nonce, &bohmac, 10); if (bohmac == nonce || bohmac[0] != '-') { retval = NONCE_BAD; goto leave; } expect = prepare_push_cert_nonce(service_dir, stamp); if (strcmp(expect, nonce)) { /* Not what we would have signed earlier */ retval = NONCE_BAD; goto leave; } /* * By how many seconds is this nonce stale? Negative value * would mean it was issued by another server with its clock * skewed in the future. */ ostamp = strtoul(push_cert_nonce, NULL, 10); nonce_stamp_slop = (long)ostamp - (long)stamp; if (nonce_stamp_slop_limit && labs(nonce_stamp_slop) <= nonce_stamp_slop_limit) { /* * Pretend as if the received nonce (which passes the * HMAC check, so it is not a forged by third-party) * is what we issued. */ free((void *)push_cert_nonce); push_cert_nonce = xstrdup(nonce); retval = NONCE_OK; } else { retval = NONCE_SLOP; } leave: free(nonce); free(expect); return retval; }
int cmd_receive_pack(int argc, const char **argv, const char *prefix) { int advertise_refs = 0; int i; struct command *commands; struct sha1_array shallow = SHA1_ARRAY_INIT; struct sha1_array ref = SHA1_ARRAY_INIT; struct shallow_info si; packet_trace_identity("receive-pack"); argv++; for (i = 1; i < argc; i++) { const char *arg = *argv++; if (*arg == '-') { if (!strcmp(arg, "--quiet")) { quiet = 1; continue; } if (!strcmp(arg, "--advertise-refs")) { advertise_refs = 1; continue; } if (!strcmp(arg, "--stateless-rpc")) { stateless_rpc = 1; continue; } if (!strcmp(arg, "--reject-thin-pack-for-testing")) { fix_thin = 0; continue; } usage(receive_pack_usage); } if (service_dir) usage(receive_pack_usage); service_dir = arg; } if (!service_dir) usage(receive_pack_usage); setup_path(); if (!enter_repo(service_dir, 0)) die("'%s' does not appear to be a git repository", service_dir); git_config(receive_pack_config, NULL); if (cert_nonce_seed) push_cert_nonce = prepare_push_cert_nonce(service_dir, time(NULL)); if (0 <= transfer_unpack_limit) unpack_limit = transfer_unpack_limit; else if (0 <= receive_unpack_limit) unpack_limit = receive_unpack_limit; if (advertise_refs || !stateless_rpc) { write_head_info(); } if (advertise_refs) return 0; if ((commands = read_head_info(&shallow)) != NULL) { const char *unpack_status = NULL; prepare_shallow_info(&si, &shallow); if (!si.nr_ours && !si.nr_theirs) shallow_update = 0; if (!delete_only(commands)) { unpack_status = unpack_with_sideband(&si); update_shallow_info(commands, &si, &ref); } execute_commands(commands, unpack_status, &si); if (pack_lockfile) unlink_or_warn(pack_lockfile); if (report_status) report(commands, unpack_status); run_receive_hook(commands, "post-receive", 1); run_update_post_hook(commands); if (auto_gc) { const char *argv_gc_auto[] = { "gc", "--auto", "--quiet", NULL, }; int opt = RUN_GIT_CMD | RUN_COMMAND_STDOUT_TO_STDERR; run_command_v_opt(argv_gc_auto, opt); } if (auto_update_server_info) update_server_info(0); clear_shallow_info(&si); } if (use_sideband) packet_flush(1); sha1_array_clear(&shallow); sha1_array_clear(&ref); free((void *)push_cert_nonce); return 0; }