From da1c9e820cd3f1e8f126d51a38e18562ada182ae Mon Sep 17 00:00:00 2001 From: David Shaw Date: Mon, 14 Oct 2002 18:50:28 +0000 Subject: [PATCH] * keyserver-internal.h, keyserver.c (print_keyrec, parse_keyrec, show_prompt, keyserver_search_prompt, keyserver_spawn): Go to version 1 of the keyserver protocol. This is a better design, similar to --with-colons, that allows for keys with multiple user IDs rather than using multiple keys. It also matches the machine readable pksd format. Also use a prettier --search-keys listing format that can fill different size windows (currently set at 24 lines). --- g10/ChangeLog | 11 + g10/keyserver-internal.h | 1 - g10/keyserver.c | 630 +++++++++++++++++++++++++-------------- 3 files changed, 420 insertions(+), 222 deletions(-) diff --git a/g10/ChangeLog b/g10/ChangeLog index 74adc7630..c832f1c83 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,14 @@ +2002-10-14 David Shaw + + * keyserver-internal.h, keyserver.c (print_keyrec, parse_keyrec, + show_prompt, keyserver_search_prompt, keyserver_spawn): Go to + version 1 of the keyserver protocol. This is a better design, + similar to --with-colons, that allows for keys with multiple user + IDs rather than using multiple keys. It also matches the machine + readable pksd format. Also use a prettier --search-keys listing + format that can fill different size windows (currently set at 24 + lines). + 2002-10-12 Werner Koch * keygen.c (print_status_key_created): New. diff --git a/g10/keyserver-internal.h b/g10/keyserver-internal.h index 9f0d2e8c0..c341578fa 100644 --- a/g10/keyserver-internal.h +++ b/g10/keyserver-internal.h @@ -17,6 +17,5 @@ int keyserver_import_fprint(const byte *fprint,size_t fprint_len); int keyserver_import_keyid(u32 *keyid); int keyserver_refresh(STRLIST users); int keyserver_search(STRLIST tokens); -void keyserver_search_prompt(IOBUF buffer,int count,const char *searchstr); #endif /* !_KEYSERVER_INTERNAL_H_ */ diff --git a/g10/keyserver.c b/g10/keyserver.c index dace0546e..59d67e32d 100644 --- a/g10/keyserver.c +++ b/g10/keyserver.c @@ -32,17 +32,26 @@ #include "i18n.h" #include "iobuf.h" #include "memory.h" +#include "ttyio.h" #include "options.h" #include "packet.h" #include "keyserver-internal.h" #include "util.h" -#define KEYSERVER_PROTO_VERSION 0 - #define GET 0 #define SEND 1 #define SEARCH 2 +struct keyrec +{ + KEYDB_SEARCH_DESC desc; + time_t createtime,expiretime; + int size,flags; + byte type; + IOBUF uidbuf; + int lines; +}; + struct kopts { char *name; @@ -61,6 +70,9 @@ struct kopts {NULL} }; +static int keyserver_work(int action,STRLIST list, + KEYDB_SEARCH_DESC *desc,int count); + void parse_keyserver_options(char *options) { @@ -218,119 +230,412 @@ parse_keyserver_uri(char *uri,const char *configname,unsigned int configlineno) return 0; } -/* Unquote only the delimiter character and backslashes (\x5C) */ -static void -printunquoted(char *string,char delim) +static void +print_keyrec(int number,struct keyrec *keyrec) { - char *ch=string; + int i; - while(*ch) + iobuf_writebyte(keyrec->uidbuf,0); + iobuf_flush_temp(keyrec->uidbuf); + printf("(%d)\t%s ",number,iobuf_get_temp_buffer(keyrec->uidbuf)); + + if(keyrec->size>0) + printf("%d bit ",keyrec->size); + + if(keyrec->type) { - if(*ch=='\\') - { - int c; + const char *str=pubkey_algo_to_string(keyrec->type); - sscanf(ch,"\\x%02x",&c); - if(c==delim) - { - printf("%c",c); - ch+=3; - } - else if(c=='\\') - { - fputc('\\',stdout); - ch+=3; - } - else - fputc(*ch,stdout); - } + if(str) + printf("%s ",str); else - fputc(*ch,stdout); - - ch++; + printf("unknown "); } -} -static int -print_keyinfo(int count,char *keystring,KEYDB_SEARCH_DESC *desc) -{ - char *certid,*userid,*keytype,*tok; - int flags,keysize=0; - time_t createtime=0,expiretime=0,modifytime=0; + switch(keyrec->desc.mode) + { + case KEYDB_SEARCH_MODE_SHORT_KID: + printf("key %08lX",(ulong)keyrec->desc.u.kid[1]); + break; - if((certid=strsep(&keystring,":"))==NULL) - return -1; + case KEYDB_SEARCH_MODE_LONG_KID: + printf("key %08lX%08lX",(ulong)keyrec->desc.u.kid[0], + (ulong)keyrec->desc.u.kid[1]); + break; - classify_user_id (certid, desc); - if(desc->mode!=KEYDB_SEARCH_MODE_SHORT_KID && - desc->mode!=KEYDB_SEARCH_MODE_LONG_KID && - desc->mode!=KEYDB_SEARCH_MODE_FPR16 && - desc->mode!=KEYDB_SEARCH_MODE_FPR20) - return -1; + case KEYDB_SEARCH_MODE_FPR16: + printf("key "); + for(i=0;i<16;i++) + printf("%02X",(unsigned char)keyrec->desc.u.fpr[i]); + break; - if((tok=strsep(&keystring,":"))==NULL) - return -1; + case KEYDB_SEARCH_MODE_FPR20: + printf("key "); + for(i=0;i<20;i++) + printf("%02X",(unsigned char)keyrec->desc.u.fpr[i]); + break; - userid=utf8_to_native(tok,strlen(tok),0); + default: + BUG(); + break; + } - if((tok=strsep(&keystring,":"))==NULL) - goto fail; + if(keyrec->createtime>0) + printf(", created %s",strtimestamp(keyrec->createtime)); - flags=atoi(tok); + if(keyrec->expiretime>0) + printf(", expires %s",strtimestamp(keyrec->expiretime)); - if((tok=strsep(&keystring,":"))==NULL) - goto fail; - - createtime=atoi(tok); - - if((tok=strsep(&keystring,":"))==NULL) - goto fail; - - expiretime=atoi(tok); - - if((tok=strsep(&keystring,":"))==NULL) - goto fail; - - modifytime=atoi(tok); - - if((keytype=strsep(&keystring,":"))==NULL) - goto fail; - - /* The last one */ - if(keystring!=NULL) - keysize=atoi(keystring); - - printf("(%d)\t",count); - - /* No need to check for control characters, as utf8_to_native does - this for us. */ - printunquoted(userid,':'); - - if(flags&1) - printf(" (revoked)"); - if(flags&2) - printf(" (disabled)"); - - printf("\n\t "); - - if(keysize>0) - printf("%d bit ",keysize); - - if(keytype[0]) - printf("%s ",keytype); - - printf("key %s, created %s",certid,strtimestamp(createtime)); - - if(expiretime>0) - printf(", expires %s",strtimestamp(expiretime)); + if(keyrec->flags&1) + printf(" (%s)",("revoked")); + if(keyrec->flags&2) + printf(" (%s)",("disabled")); + if(keyrec->flags&4) + printf(" (%s)",("expired")); printf("\n"); +} + +/* Returns a keyrec (which must be freed) once a key is complete, and + NULL otherwise. Call with a NULL keystring once key parsing is + complete to return any unfinished keys. */ +static struct keyrec * +parse_keyrec(char *keystring) +{ + static struct keyrec *work=NULL; + struct keyrec *ret=NULL; + char *record,*tok; + int i; + + if(keystring==NULL) + { + if(work==NULL) + return NULL; + else if(work->desc.mode==KEYDB_SEARCH_MODE_NONE) + { + m_free(work); + return NULL; + } + else + { + ret=work; + work=NULL; + return ret; + } + } + + if(work==NULL) + { + work=m_alloc_clear(sizeof(struct keyrec)); + work->uidbuf=iobuf_temp(); + } + + /* Remove trailing whitespace */ + for(i=strlen(keystring);i>0;i--) + if(isspace(keystring[i-1])) + keystring[i-1]='\0'; + else + break; + + if((record=strsep(&keystring,":"))==NULL) + return ret; + + if(ascii_strcasecmp("pub",record)==0) + { + if(work->desc.mode) + { + ret=work; + work=m_alloc_clear(sizeof(struct keyrec)); + work->uidbuf=iobuf_temp(); + } + + if((tok=strsep(&keystring,":"))==NULL) + return ret; + + classify_user_id(tok,&work->desc); + if(work->desc.mode!=KEYDB_SEARCH_MODE_SHORT_KID + && work->desc.mode!=KEYDB_SEARCH_MODE_LONG_KID + && work->desc.mode!=KEYDB_SEARCH_MODE_FPR16 + && work->desc.mode!=KEYDB_SEARCH_MODE_FPR20) + { + work->desc.mode=KEYDB_SEARCH_MODE_NONE; + return ret; + } + + /* Note all items after this are optional. This allows us to + have a pub line as simple as pub:keyid and nothing else. */ + + work->lines++; + + if((tok=strsep(&keystring,":"))==NULL) + return ret; + + work->type=atoi(tok); + + if((tok=strsep(&keystring,":"))==NULL) + return ret; + + work->size=atoi(tok); + + if((tok=strsep(&keystring,":"))==NULL) + return ret; + + work->createtime=atoi(tok); + + if((tok=strsep(&keystring,":"))==NULL) + return ret; + + work->expiretime=atoi(tok); + + if((tok=strsep(&keystring,":"))==NULL) + return ret; + + while(*tok) + switch(*tok++) + { + case 'r': + case 'R': + work->flags|=1; + break; + + case 'd': + case 'D': + work->flags|=2; + break; + + case 'e': + case 'E': + work->flags|=4; + break; + } + + if(work->expiretime && work->expiretime<=make_timestamp()) + work->flags|=4; + } + else if(ascii_strcasecmp("uid",record)==0 && work->desc.mode) + { + char *userid,*tok,*decoded; + int i=0; + + if((tok=strsep(&keystring,":"))==NULL) + return ret; + + if(strlen(tok)==0) + return ret; + + userid=tok; + + /* By definition, de-%-encoding is always smaller than the + original string so we can decode in place. */ + + while(*tok) + if(tok[0]=='%' && tok[1] && tok[2]) + { + if((userid[i]=hextobyte(&tok[1]))==-1) + userid[i]='?'; + + i++; + tok+=3; + } + else + userid[i++]=*tok++; + + /* We don't care about the other info provided in the uid: line + since no keyserver supports marking userids with timestamps + or revoked/expired/disabled yet. */ + + /* No need to check for control characters, as utf8_to_native + does this for us. */ + + decoded=utf8_to_native(userid,i,0); + iobuf_writestr(work->uidbuf,decoded); + m_free(decoded); + iobuf_writestr(work->uidbuf,"\n\t"); + work->lines++; + } + + /* Ignore any records other than "pri" and "uid" for easy future + growth. */ + + return ret; +} + +/* TODO: do this as a list sent to keyserver_work rather than calling + it once for each key to get the correct counts after the import + (cosmetics, really) and to better take advantage of the keyservers + that can do multiple fetches in one go (LDAP). */ +static int +show_prompt(KEYDB_SEARCH_DESC *desc,int numdesc,int count,const char *search) +{ + char *answer; + + if(count) + { + static int from=1; + tty_printf("Keys %d-%d of %d for \"%s\". ",from,numdesc,count,search); + from=numdesc+1; + } + + answer=cpr_get_no_help("keysearch.prompt", + _("Enter number(s), N)ext, or Q)uit > ")); + /* control-d */ + if(answer[0]=='\x04') + { + printf("Q\n"); + answer[0]='q'; + } + + if(answer[0]=='q' || answer[0]=='Q') + { + m_free(answer); + return 1; + } + else if(atoi(answer)>=1 && atoi(answer)<=numdesc) + { + char *split=answer,*num; + + while((num=strsep(&split," ,"))!=NULL) + if(atoi(num)>=1 && atoi(num)<=numdesc) + keyserver_work(GET,NULL,&desc[atoi(num)-1],1); + + m_free(answer); + return 1; + } return 0; +} - fail: - m_free (userid); - return -1; +/* Count and searchstr are just for cosmetics. If the count is too + small, it will grow safely. If negative it disables the "Key x-y + of z" messages. */ +static void +keyserver_search_prompt(IOBUF buffer,const char *searchstr) +{ + int i=0,validcount=0,started=0,count=1; + unsigned int maxlen,buflen; + KEYDB_SEARCH_DESC *desc; + byte *line=NULL; + /* TODO: Something other than 23? That's 24-1 (the prompt). */ + int maxlines=23,numlines=0; + + desc=m_alloc(count*sizeof(KEYDB_SEARCH_DESC)); + + for(;;) + { + struct keyrec *keyrec; + int rl; + + maxlen=1024; + rl=iobuf_read_line(buffer,&line,&buflen,&maxlen); + + /* Look for an info: line. The only current info: values + defined are the version and key count. */ + if(!started && rl>0 && ascii_strncasecmp("info:",line,5)==0) + { + char *tok,*str=&line[5]; + + if((tok=strsep(&str,":"))!=NULL) + { + int version; + + if(sscanf(tok,"%d",&version)!=1) + version=1; + + if(version!=1) + { + log_error(_("invalid keyserver protocol " + "(us %d!=handler %d)\n"),1,version); + break; + } + } + + if((tok=strsep(&str,":"))!=NULL && sscanf(tok,"%d",&count)==1) + { + if(count==0) + goto notfound; + else if(count<0) + count=10; + else + validcount=1; + + desc=m_realloc(desc,count*sizeof(KEYDB_SEARCH_DESC)); + } + + started=1; + continue; + } + + if(rl==0) + { + keyrec=parse_keyrec(NULL); + + if(keyrec==NULL) + { + if(i==0) + { + count=0; + break; + } + + if(i!=count) + validcount=0; + + for(;;) + { + if(show_prompt(desc,i,validcount?count:0,searchstr)) + break; + validcount=0; + } + + break; + } + } + else + keyrec=parse_keyrec(line); + + if(i==count) + { + /* keyserver helper sent more keys than they claimed in the + info: line. */ + count+=10; + desc=m_realloc(desc,count*sizeof(KEYDB_SEARCH_DESC)); + validcount=0; + } + + if(keyrec) + { + desc[i]=keyrec->desc; + + if(numlines+keyrec->lines>maxlines) + { + if(show_prompt(desc,i,validcount?count:0,searchstr)) + break; + else + numlines=0; + } + + print_keyrec(i+1,keyrec); + numlines+=keyrec->lines; + iobuf_close(keyrec->uidbuf); + m_free(keyrec); + + started=1; + i++; + } + } + + m_free(desc); + m_free(line); + + notfound: + if(count==0) + { + if(searchstr) + log_info(_("key \"%s\" not found on keyserver\n"),searchstr); + else + log_info(_("key not found on keyserver\n")); + return; + } } #define KEYSERVER_ARGS_KEEP " -o \"%O\" \"%I\"" @@ -549,8 +854,8 @@ keyserver_spawn(int action,STRLIST list, for(;;) { - char *ptr; int plen; + char *ptr; maxlen=1024; if(iobuf_read_line(spawn->fromchild,&line,&buflen,&maxlen)==0) @@ -628,19 +933,7 @@ keyserver_spawn(int action,STRLIST list, case SEARCH: { - /* Look for the COUNT line */ - do - { - maxlen=1024; - if(iobuf_read_line(spawn->fromchild,&line,&buflen,&maxlen)==0) - { - ret=G10ERR_READ_FILE; - goto fail; /* i.e. EOF */ - } - } - while(sscanf(line,"COUNT %d\n",&i)!=1); - - keyserver_search_prompt(spawn->fromchild,i,searchstr); + keyserver_search_prompt(spawn->fromchild,searchstr); break; } @@ -693,6 +986,12 @@ keyserver_work(int action,STRLIST list,KEYDB_SEARCH_DESC *desc,int count) action==GET?"get":action==SEND?"send": action==SEARCH?"search":"unknown", opt.keyserver_scheme); + break; + + case KEYSERVER_VERSION_ERROR: + log_error(_("gpgkeys_%s does not support handler version %d\n"), + opt.keyserver_scheme,KEYSERVER_PROTO_VERSION); + break; case KEYSERVER_INTERNAL_ERROR: default: @@ -963,114 +1262,3 @@ keyserver_search(STRLIST tokens) else return 0; } - -/* Count and searchstr are just for cosmetics. If the count is too - small, it will grow safely. If negative it disables the "Key x-y - of z" messages. */ - -/* TODO: do this as a list sent to keyserver_work rather than calling - it once for each key to get the correct counts after the import - (cosmetics, really) and to better take advantage of the keyservers - that can do multiple fetches in one go (LDAP). */ -void -keyserver_search_prompt(IOBUF buffer,int count,const char *searchstr) -{ - int i=0,validcount=1; - unsigned int maxlen,buflen; - KEYDB_SEARCH_DESC *desc; - byte *line=NULL; - char *answer; - - if(count==0) - goto notfound; - - if(count<0) - { - validcount=0; - count=10; - } - - desc=m_alloc(count*sizeof(KEYDB_SEARCH_DESC)); - - /* Read each line and show it to the user */ - - for(;;) - { - int rl; - - if(validcount && i%10==0) - { - printf("Keys %d-%d of %d",i+1,(i+100) - { - if(print_keyinfo(i+1,line,&desc[i])==0) - { - i++; - - if(i==count) - { - count+=10; - desc=m_realloc(desc,count*sizeof(KEYDB_SEARCH_DESC)); - validcount=0; - } - } - else - continue; - } - - if(rl==0 && i==0) - { - count=0; - break; - } - - if(i%10==0 || rl==0) - { - answer=cpr_get_no_help("keysearch.prompt", - _("Enter number(s), N)ext, or Q)uit > ")); - /* control-d */ - if(answer[0]=='\x04') - { - printf("Q\n"); - answer[0]='q'; - } - - if(answer[0]=='q' || answer[0]=='Q') - { - m_free(answer); - break; - } - else if(atoi(answer)>=1 && atoi(answer)<=i) - { - char *split=answer,*num; - - while((num=strsep(&split," ,"))!=NULL) - if(atoi(num)>=1 && atoi(num)<=i) - keyserver_work(GET,NULL,&desc[atoi(num)-1],1); - - m_free(answer); - break; - } - } - } - - m_free(desc); - m_free(line); - - notfound: - if(count==0) - { - if(searchstr) - log_info(_("key \"%s\" not found on keyserver\n"),searchstr); - else - log_info(_("key not found on keyserver\n")); - return; - } -}