/* cert.c - DNS CERT code * Copyright (C) 2005 Free Software Foundation, Inc. * * This file is part of GNUPG. * * GNUPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GNUPG 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #include <config.h> #include <sys/types.h> #ifdef USE_DNS_CERT #ifdef _WIN32 #include <windows.h> #else #include <netinet/in.h> #include <arpa/nameser.h> #include <resolv.h> #endif #include <string.h> #include "memory.h" #endif #include "iobuf.h" /* Not every installation has gotten around to supporting CERTs yet... */ #ifndef T_CERT #define T_CERT 37 #endif #ifdef USE_DNS_CERT /* Returns -1 on error, 0 for no answer, 1 for PGP provided and 2 for IPGP provided. */ int get_cert(const char *name,size_t max_size,IOBUF *iobuf,char **url) { unsigned char *answer; int r,ret=-1; u16 count; answer=xmalloc(max_size); r=res_query(name,C_IN,T_CERT,answer,max_size); /* Not too big, not too small, no errors and at least 1 answer. */ if(r>=sizeof(HEADER) && r<=max_size && (((HEADER *)answer)->rcode)==NOERROR && (count=ntohs(((HEADER *)answer)->ancount))) { int rc; unsigned char *pt,*emsg; emsg=&answer[r]; pt=&answer[sizeof(HEADER)]; /* Skip over the query */ rc=dn_skipname(pt,emsg); if(rc==-1) goto fail; pt+=rc+QFIXEDSZ; /* There are several possible response types for a CERT request. We're interested in the PGP (a key) and IPGP (a URI) types. Skip all others. TODO: A key is better than a URI since we've gone through all this bother to fetch it, so favor that if we have both PGP and IPGP? */ while(count-->0 && pt<emsg) { u16 type,class,dlen,ctype; rc=dn_skipname(pt,emsg); /* the name we just queried for */ if(rc==-1) break; pt+=rc; /* Truncated message? */ if((emsg-pt)<15) break; type=*pt++ << 8; type|=*pt++; class=*pt++ << 8; class|=*pt++; /* We asked for IN and got something else !? */ if(class!=C_IN) break; /* ttl */ pt+=4; /* data length */ dlen=*pt++ << 8; dlen|=*pt++; /* We asked for CERT and got something else - might be a CNAME, so loop around again. */ if(type!=T_CERT) { pt+=dlen; continue; } /* The CERT type */ ctype=*pt++ << 8; ctype|=*pt++; /* Skip the CERT key tag and algo which we don't need. */ pt+=3; dlen-=5; if(ctype==3 && iobuf) { /* PGP type */ *iobuf=iobuf_temp_with_content((char *)pt,dlen); ret=1; break; } #if 0 else if(ctype==6 && dlen<1023 && url) { /* Sanity check the IPGP URL type that the URL isn't too long */ *url=xmalloc(dlen+1); memcpy(*url,pt,dlen); (*url)[dlen]='\0'; ret=2; break; } #endif /* Neither type matches, so go around to the next answer. */ pt+=dlen; } } fail: xfree(answer); return ret; } #else /* !USE_DNS_CERT */ int get_cert(const char *name,size_t max_size,IOBUF *iobuf,char **url) { return -1; } #endif /* Test with simon.josefsson.org */ #ifdef TEST int main(int argc,char *argv[]) { char *url; int rc; IOBUF iobuf; if(argc!=2) { printf("cert-test [name]\n"); return 1; } printf("CERT lookup on %s\n",argv[1]); rc=get_cert(argv[1],16384,&iobuf,&url); if(rc==-1) printf("error\n"); else if(rc==0) printf("no answer\n"); else if(rc==1) { printf("key found: %d bytes\n",iobuf_get_temp_length(iobuf)); iobuf_close(iobuf); } else if(rc==2) { printf("URL found: %s\n",url); xfree(url); } return 0; } #endif /* TEST */