1#include"cache.h" 2#include"tag.h" 3#include"exec_cmd.h" 4 5/* 6 * A signature file has a very simple fixed format: four lines 7 * of "object <sha1>" + "type <typename>" + "tag <tagname>" + 8 * "tagger <committer>", followed by a blank line, a free-form tag 9 * message and a signature block that git itself doesn't care about, 10 * but that can be verified with gpg or similar. 11 * 12 * The first four lines are guaranteed to be at least 83 bytes: 13 * "object <sha1>\n" is 48 bytes, "type tag\n" at 9 bytes is the 14 * shortest possible type-line, "tag .\n" at 6 bytes is the shortest 15 * single-character-tag line, and "tagger . <> 0 +0000\n" at 20 bytes is 16 * the shortest possible tagger-line. 17 */ 18 19/* 20 * We refuse to tag something we can't verify. Just because. 21 */ 22static intverify_object(unsigned char*sha1,const char*expected_type) 23{ 24int ret = -1; 25enum object_type type; 26unsigned long size; 27void*buffer =read_sha1_file(sha1, &type, &size); 28 29if(buffer) { 30if(type ==type_from_string(expected_type)) 31 ret =check_sha1_signature(sha1, buffer, size, expected_type); 32free(buffer); 33} 34return ret; 35} 36 37#ifdef NO_C99_FORMAT 38#define PD_FMT"%d" 39#else 40#define PD_FMT"%td" 41#endif 42 43static intverify_tag(char*buffer,unsigned long size) 44{ 45int typelen; 46char type[20]; 47unsigned char sha1[20]; 48const char*object, *type_line, *tag_line, *tagger_line, *lb, *rb; 49size_t len; 50 51if(size <84) 52returnerror("wanna fool me ? you obviously got the size wrong !"); 53 54 buffer[size] =0; 55 56/* Verify object line */ 57 object = buffer; 58if(memcmp(object,"object ",7)) 59returnerror("char%d: does not start with\"object\"",0); 60 61if(get_sha1_hex(object +7, sha1)) 62returnerror("char%d: could not get SHA1 hash",7); 63 64/* Verify type line */ 65 type_line = object +48; 66if(memcmp(type_line -1,"\ntype ",6)) 67returnerror("char%d: could not find\"\\ntype\"",47); 68 69/* Verify tag-line */ 70 tag_line =strchr(type_line,'\n'); 71if(!tag_line) 72returnerror("char" PD_FMT ": could not find next\"\\n\"", type_line - buffer); 73 tag_line++; 74if(memcmp(tag_line,"tag ",4) || tag_line[4] =='\n') 75returnerror("char" PD_FMT ": no\"tag\"found", tag_line - buffer); 76 77/* Get the actual type */ 78 typelen = tag_line - type_line -strlen("type\n"); 79if(typelen >=sizeof(type)) 80returnerror("char" PD_FMT ": type too long", type_line+5- buffer); 81 82memcpy(type, type_line+5, typelen); 83 type[typelen] =0; 84 85/* Verify that the object matches */ 86if(verify_object(sha1, type)) 87returnerror("char%d: could not verify object%s",7,sha1_to_hex(sha1)); 88 89/* Verify the tag-name: we don't allow control characters or spaces in it */ 90 tag_line +=4; 91for(;;) { 92unsigned char c = *tag_line++; 93if(c =='\n') 94break; 95if(c >' ') 96continue; 97returnerror("char" PD_FMT ": could not verify tag name", tag_line - buffer); 98} 99 100/* Verify the tagger line */ 101 tagger_line = tag_line; 102 103if(memcmp(tagger_line,"tagger ",7)) 104returnerror("char" PD_FMT ": could not find\"tagger\"", 105 tagger_line - buffer); 106 107/* 108 * Check for correct form for name and email 109 * i.e. " <" followed by "> " on _this_ line 110 * No angle brackets within the name or email address fields. 111 * No spaces within the email address field. 112 */ 113 tagger_line +=7; 114if(!(lb =strstr(tagger_line," <")) || !(rb =strstr(lb+2,"> ")) || 115strpbrk(tagger_line,"<>\n") != lb+1|| 116strpbrk(lb+2,"><\n") != rb) 117returnerror("char" PD_FMT ": malformed tagger field", 118 tagger_line - buffer); 119 120/* Check for author name, at least one character, space is acceptable */ 121if(lb == tagger_line) 122returnerror("char" PD_FMT ": missing tagger name", 123 tagger_line - buffer); 124 125/* timestamp, 1 or more digits followed by space */ 126 tagger_line = rb +2; 127if(!(len =strspn(tagger_line,"0123456789"))) 128returnerror("char" PD_FMT ": missing tag timestamp", 129 tagger_line - buffer); 130 tagger_line += len; 131if(*tagger_line !=' ') 132returnerror("char" PD_FMT ": malformed tag timestamp", 133 tagger_line - buffer); 134 tagger_line++; 135 136/* timezone, 5 digits [+-]hhmm, max. 1400 */ 137if(!((tagger_line[0] =='+'|| tagger_line[0] =='-') && 138strspn(tagger_line+1,"0123456789") ==4&& 139 tagger_line[5] =='\n'&&atoi(tagger_line+1) <=1400)) 140returnerror("char" PD_FMT ": malformed tag timezone", 141 tagger_line - buffer); 142 tagger_line +=6; 143 144/* Verify the blank line separating the header from the body */ 145if(*tagger_line !='\n') 146returnerror("char" PD_FMT ": trailing garbage in tag header", 147 tagger_line - buffer); 148 149/* The actual stuff afterwards we don't care about.. */ 150return0; 151} 152 153#undef PD_FMT 154 155intmain(int argc,char**argv) 156{ 157struct strbuf buf = STRBUF_INIT; 158unsigned char result_sha1[20]; 159 160if(argc !=1) 161usage("git mktag < signaturefile"); 162 163git_extract_argv0_path(argv[0]); 164 165setup_git_directory(); 166 167if(strbuf_read(&buf,0,4096) <0) { 168die_errno("could not read from stdin"); 169} 170 171/* Verify it for some basic sanity: it needs to start with 172 "object <sha1>\ntype\ntagger " */ 173if(verify_tag(buf.buf, buf.len) <0) 174die("invalid tag signature file"); 175 176if(write_sha1_file(buf.buf, buf.len, tag_type, result_sha1) <0) 177die("unable to write tag file"); 178 179strbuf_release(&buf); 180printf("%s\n",sha1_to_hex(result_sha1)); 181return0; 182}