aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Shaw <[email protected]>2004-02-22 00:08:53 +0000
committerDavid Shaw <[email protected]>2004-02-22 00:08:53 +0000
commit3b9d7a6430eb8c5ffe15326ab181e110f901561a (patch)
treece6ff09544b0fb158bd0edcd0a35899457679bfb
parent* util.h: Prototype for hextobyte(). (diff)
downloadgnupg-3b9d7a6430eb8c5ffe15326ab181e110f901561a.tar.gz
gnupg-3b9d7a6430eb8c5ffe15326ab181e110f901561a.zip
* gpgkeys_ldap.c (epoch2ldaptime): New. Converse of ldap2epochtime.
(make_one_attr): New. Build a modification list in memory to send to the LDAP server. (build_attrs): New. Parse INFO lines sent over by gpg. (free_mod_values): New. Unwinds a modification list. (send_key_keyserver): Renamed from old send_key(). (send_key): New function to send a key to a LDAP server. (main): Use send_key() for real LDAP servers, send_key_keyserver() otherwise.
-rw-r--r--keyserver/ChangeLog13
-rw-r--r--keyserver/gpgkeys_ldap.c578
2 files changed, 534 insertions, 57 deletions
diff --git a/keyserver/ChangeLog b/keyserver/ChangeLog
index 2724b7b29..0b529114b 100644
--- a/keyserver/ChangeLog
+++ b/keyserver/ChangeLog
@@ -1,3 +1,16 @@
+2004-02-21 David Shaw <[email protected]>
+
+ * gpgkeys_ldap.c (epoch2ldaptime): New. Converse of
+ ldap2epochtime.
+ (make_one_attr): New. Build a modification list in memory to send
+ to the LDAP server.
+ (build_attrs): New. Parse INFO lines sent over by gpg.
+ (free_mod_values): New. Unwinds a modification list.
+ (send_key_keyserver): Renamed from old send_key().
+ (send_key): New function to send a key to a LDAP server.
+ (main): Use send_key() for real LDAP servers, send_key_keyserver()
+ otherwise.
+
2004-02-20 David Shaw <[email protected]>
* gpgkeys_ldap.c: Replacement prototypes for setenv and unsetenv.
diff --git a/keyserver/gpgkeys_ldap.c b/keyserver/gpgkeys_ldap.c
index afc241f25..1dcf8c643 100644
--- a/keyserver/gpgkeys_ldap.c
+++ b/keyserver/gpgkeys_ldap.c
@@ -29,6 +29,7 @@
#include <stdlib.h>
#include <errno.h>
#include <ldap.h>
+#include "util.h"
#include "keyserver.h"
#ifdef __riscos__
@@ -41,7 +42,7 @@ extern int optind;
#define GET 0
#define SEND 1
#define SEARCH 2
-#define MAX_LINE 80
+#define MAX_LINE 256
static int verbose=0,include_disabled=0,include_revoked=0,include_subkeys=0;
static int real_ldap=0;
@@ -66,7 +67,7 @@ struct keylist
struct keylist *next;
};
-int
+static int
ldap_err_to_gpg_err(int err)
{
int ret;
@@ -89,7 +90,7 @@ ldap_err_to_gpg_err(int err)
return ret;
}
-int
+static int
ldap_to_gpg_err(LDAP *ld)
{
#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_ERROR_NUMBER)
@@ -114,7 +115,7 @@ ldap_to_gpg_err(LDAP *ld)
#endif
}
-int
+static int
key_in_keylist(const char *key,struct keylist *list)
{
struct keylist *keyptr=list;
@@ -130,7 +131,7 @@ key_in_keylist(const char *key,struct keylist *list)
return 0;
}
-int
+static int
add_key_to_keylist(const char *key,struct keylist **list)
{
struct keylist *keyptr=malloc(sizeof(struct keylist));
@@ -150,7 +151,7 @@ add_key_to_keylist(const char *key,struct keylist **list)
return 0;
}
-void
+static void
free_keylist(struct keylist *list)
{
while(list!=NULL)
@@ -162,10 +163,509 @@ free_keylist(struct keylist *list)
}
}
-int
+static time_t
+ldap2epochtime(const char *timestr)
+{
+ struct tm pgptime;
+ time_t answer;
+
+ memset(&pgptime,0,sizeof(pgptime));
+
+ /* YYYYMMDDHHmmssZ */
+
+ sscanf(timestr,"%4d%2d%2d%2d%2d%2d",
+ &pgptime.tm_year,
+ &pgptime.tm_mon,
+ &pgptime.tm_mday,
+ &pgptime.tm_hour,
+ &pgptime.tm_min,
+ &pgptime.tm_sec);
+
+ pgptime.tm_year-=1900;
+ pgptime.tm_isdst=-1;
+ pgptime.tm_mon--;
+
+ /* mktime takes the timezone into account, and we can't have that.
+ I'd use timegm, but it's not portable. */
+
+#ifdef HAVE_TIMEGM
+ answer=timegm(&pgptime);
+#else
+ {
+ char *zone=getenv("TZ");
+ setenv("TZ","UTC",1);
+ tzset();
+ answer=mktime(&pgptime);
+ if(zone)
+ setenv("TZ",zone,1);
+ else
+ unsetenv("TZ");
+ tzset();
+ }
+#endif
+
+ return answer;
+}
+
+/* Caller must free */
+static char *
+epoch2ldaptime(time_t stamp)
+{
+ struct tm *ldaptime;
+ char buf[16];
+
+ ldaptime=gmtime(&stamp);
+
+ ldaptime->tm_year+=1900;
+ ldaptime->tm_mon++;
+
+ /* YYYYMMDDHHmmssZ */
+
+ sprintf(buf,"%04d%02d%02d%02d%02d%02dZ",
+ ldaptime->tm_year,
+ ldaptime->tm_mon,
+ ldaptime->tm_mday,
+ ldaptime->tm_hour,
+ ldaptime->tm_min,
+ ldaptime->tm_sec);
+
+ return strdup(buf);
+}
+
+static int
+make_one_attr(LDAPMod ***modlist,char *attr,const char *value)
+{
+ LDAPMod **m;
+ int nummods=0;
+
+ /* Search modlist for the attribute we're playing with. */
+ for(m=*modlist;*m;m++)
+ {
+ if(strcmp((*m)->mod_type,attr)==0)
+ {
+ char **ptr=(*m)->mod_values;
+ int numvalues=0;
+
+ if(ptr)
+ while(*ptr++)
+ numvalues++;
+
+ ptr=realloc((*m)->mod_values,sizeof(char *)*(numvalues+2));
+ if(!ptr)
+ return 0;
+
+ (*m)->mod_values=ptr;
+ ptr[numvalues]=strdup(value);
+ if(!ptr[numvalues])
+ return 0;
+
+ ptr[numvalues+1]=NULL;
+ break;
+ }
+
+ nummods++;
+ }
+
+ /* We didn't find the attr, so make one and add it to the end */
+ if(!*m)
+ {
+ LDAPMod **grow;
+
+ grow=realloc(*modlist,sizeof(LDAPMod *)*(nummods+2));
+ if(!grow)
+ return 0;
+
+ *modlist=grow;
+ grow[nummods]=malloc(sizeof(LDAPMod));
+ if(!grow[nummods])
+ return 0;
+ grow[nummods]->mod_op=LDAP_MOD_REPLACE;
+ grow[nummods]->mod_type=attr;
+ grow[nummods]->mod_values=malloc(sizeof(char *)*2);
+ if(!grow[nummods]->mod_values)
+ {
+ grow[nummods]=NULL;
+ return 0;
+ }
+
+ /* Is this the right thing? Can a UTF8-encoded user ID have
+ embedded nulls? */
+ grow[nummods]->mod_values[0]=strdup(value);
+ if(!grow[nummods]->mod_values[0])
+ {
+ free(grow[nummods]->mod_values);
+ grow[nummods]=NULL;
+ return 0;
+ }
+
+ grow[nummods]->mod_values[1]=NULL;
+ grow[nummods+1]=NULL;
+ }
+
+ return 1;
+}
+
+static void
+build_attrs(LDAPMod ***modlist,char *line)
+{
+ char *record;
+ int i;
+
+ /* Remove trailing whitespace */
+ for(i=strlen(line);i>0;i--)
+ if(ascii_isspace(line[i-1]))
+ line[i-1]='\0';
+ else
+ break;
+
+ if((record=strsep(&line,":"))==NULL)
+ return;
+
+ if(ascii_strcasecmp("pub",record)==0)
+ {
+ char *tok;
+ int disabled=0,revoked=0;
+
+ /* The long keyid */
+ if((tok=strsep(&line,":"))==NULL)
+ return;
+
+ if(strlen(tok)==16)
+ {
+ make_one_attr(modlist,"pgpCertID",tok);
+ make_one_attr(modlist,"pgpKeyID",&tok[8]);
+ }
+ else
+ return;
+
+ /* The primary pubkey algo */
+ if((tok=strsep(&line,":"))==NULL)
+ return;
+
+ switch(atoi(tok))
+ {
+ case 1:
+ make_one_attr(modlist,"pgpKeyType","RSA");
+ break;
+
+ case 17:
+ make_one_attr(modlist,"pgpKeyType","DSS/DH");
+ break;
+ }
+
+ /* Size of primary key */
+ if((tok=strsep(&line,":"))==NULL)
+ return;
+
+ if(atoi(tok)>0)
+ {
+ char padded[6];
+ int val=atoi(tok);
+
+ /* We zero pad this on the left to make PGP happy. */
+
+ if(val<99999 && val>0)
+ {
+ sprintf(padded,"%05u",atoi(tok));
+ make_one_attr(modlist,"pgpKeySize",padded);
+ }
+ }
+
+ /* pk timestamp */
+ if((tok=strsep(&line,":"))==NULL)
+ return;
+
+ if(atoi(tok)>0)
+ {
+ char *stamp=epoch2ldaptime(atoi(tok));
+ if(stamp)
+ {
+ make_one_attr(modlist,"pgpKeyCreateTime",tok);
+ free(stamp);
+ }
+ }
+
+ /* pk expire */
+ if((tok=strsep(&line,":"))==NULL)
+ return;
+
+ if(atoi(tok)>0)
+ {
+ char *stamp=epoch2ldaptime(atoi(tok));
+ if(stamp)
+ {
+ make_one_attr(modlist,"pgpKeyExpireTime",tok);
+ free(stamp);
+ }
+ }
+
+ /* flags */
+ if((tok=strsep(&line,":"))==NULL)
+ return;
+
+ while(*tok)
+ switch(*tok++)
+ {
+ case 'r':
+ case 'R':
+ revoked=1;
+ break;
+
+ case 'd':
+ case 'D':
+ disabled=1;
+ break;
+ }
+
+ /*
+ Note that we always create the pgpDisabled and pgpRevoked
+ attributes, regardless of whether the key is disabled/revoked
+ or not. This is because a very common search is like
+ "(&(pgpUserID=*isabella*)(pgpDisabled=0))"
+ */
+
+ make_one_attr(modlist,"pgpDisabled",disabled?"1":"0");
+ make_one_attr(modlist,"pgpRevoked",revoked?"1":"0");
+ }
+ else if(ascii_strcasecmp("uid",record)==0)
+ {
+ char *userid,*tok;
+
+ /* The user ID string */
+ if((tok=strsep(&line,":"))==NULL)
+ return;
+
+ if(strlen(tok)==0)
+ return;
+
+ userid=tok;
+
+ /* By definition, de-%-encoding is always smaller than the
+ original string so we can decode in place. */
+
+ i=0;
+
+ 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 the LDAP schema doesn't need it. */
+
+ make_one_attr(modlist,"pgpUserID",userid);
+ }
+}
+
+static void
+free_mod_values(LDAPMod *mod)
+{
+ char **ptr;
+
+ if(!mod->mod_values)
+ return;
+
+ for(ptr=mod->mod_values;*ptr;ptr++)
+ {
+ // printf("freeing %s with %s as item\n",mod->mod_type,*ptr);
+ free(*ptr);
+ }
+
+ free(mod->mod_values);
+}
+
+static int
send_key(int *eof)
{
int err,begin=0,end=0,keysize=1,ret=KEYSERVER_INTERNAL_ERROR;
+ char *dn=NULL,line[MAX_LINE],*key=NULL;
+ char keyid[17];
+ LDAPMod **modlist,**ml;
+
+ modlist=malloc(sizeof(LDAPMod *));
+ if(!modlist)
+ {
+ fprintf(console,"gpgkeys: can't allocate memory for keyserver record\n");
+ ret=KEYSERVER_NO_MEMORY;
+ goto fail;
+ }
+
+ *modlist=NULL;
+
+ /* Assemble the INFO stuff into LDAP attributes */
+
+ while(fgets(line,MAX_LINE,input)!=NULL)
+ if(sscanf(line,"INFO %16s BEGIN\n",keyid)==1)
+ {
+ begin=1;
+ break;
+ }
+
+ if(!begin)
+ {
+ /* i.e. eof before the INFO BEGIN was found. This isn't an
+ error. */
+ *eof=1;
+ ret=KEYSERVER_OK;
+ goto fail;
+ }
+
+ if(strlen(keyid)!=16)
+ {
+ printf("bad\n");
+ *eof=1;
+ ret=KEYSERVER_KEY_INCOMPLETE;
+ goto fail;
+ }
+
+ dn=malloc(strlen("pgpCertID=")+16+1+strlen(basekeyspacedn)+1);
+ if(dn==NULL)
+ {
+ fprintf(console,"gpgkeys: can't allocate memory for keyserver record\n");
+ ret=KEYSERVER_NO_MEMORY;
+ goto fail;
+ }
+
+ sprintf(dn,"pgpCertID=%s,%s",keyid,basekeyspacedn);
+
+ key=malloc(1);
+ if(!key)
+ {
+ fprintf(console,"gpgkeys: unable to allocate memory for key\n");
+ ret=KEYSERVER_NO_MEMORY;
+ goto fail;
+ }
+
+ key[0]='\0';
+
+ /* Now parse each line until we see the END */
+
+ while(fgets(line,MAX_LINE,input)!=NULL)
+ if(sscanf(line,"INFO %16s END\n",keyid)==1)
+ {
+ end=1;
+ break;
+ }
+ else
+ {
+ build_attrs(&modlist,line);
+ // printf("line %s\n",line);
+ }
+
+ if(!end)
+ {
+ fprintf(console,"gpgkeys: no INFO %s END found\n",keyid);
+ *eof=1;
+ ret=KEYSERVER_KEY_INCOMPLETE;
+ goto fail;
+ }
+
+ begin=end=0;
+
+ /* Read and throw away stdin until we see the BEGIN */
+
+ while(fgets(line,MAX_LINE,input)!=NULL)
+ if(sscanf(line,"KEY %16s BEGIN\n",keyid)==1)
+ {
+ begin=1;
+ break;
+ }
+
+ if(!begin)
+ {
+ /* i.e. eof before the KEY BEGIN was found. This isn't an
+ error. */
+ *eof=1;
+ ret=KEYSERVER_OK;
+ goto fail;
+ }
+
+ /* Now slurp up everything until we see the END */
+
+ while(fgets(line,MAX_LINE,input)!=NULL)
+ if(sscanf(line,"KEY %16s END\n",keyid)==1)
+ {
+ end=1;
+ break;
+ }
+ else
+ {
+ char *tempkey;
+ keysize+=strlen(line);
+ tempkey=realloc(key,keysize);
+ if(tempkey==NULL)
+ {
+ fprintf(console,"gpgkeys: unable to reallocate for key\n");
+ ret=KEYSERVER_NO_MEMORY;
+ goto fail;
+ }
+ else
+ key=tempkey;
+
+ strcat(key,line);
+ }
+
+ if(!end)
+ {
+ fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
+ *eof=1;
+ ret=KEYSERVER_KEY_INCOMPLETE;
+ goto fail;
+ }
+
+ make_one_attr(&modlist,"objectClass","pgpKeyInfo");
+ make_one_attr(&modlist,"pgpKey",key);
+
+ err=ldap_add_s(ldap,dn,modlist);
+
+ /* If it's there already, we just turn around and send a modify
+ command for the same key to bring it into compliance with our
+ copy. Note that unlike the LDAP keyserver (and really, any other
+ keyserver) this does NOT merge signatures, but replaces the whole
+ key. This should make some people very happy. */
+
+ if(err==LDAP_ALREADY_EXISTS)
+ err=ldap_modify_s(ldap,dn,modlist);
+
+ if(err!=LDAP_SUCCESS)
+ {
+ printf("err %d\n",err);
+ fprintf(console,"gpgkeys: error adding key %s to keyserver: %s\n",
+ keyid,ldap_err2string(err));
+ ret=ldap_err_to_gpg_err(err);
+ goto fail;
+ }
+
+ ret=KEYSERVER_OK;
+
+ fail:
+ /* Unwind and free the whole modlist structure */
+ for(ml=modlist;*ml;ml++)
+ {
+ free_mod_values(*ml);
+ free(*ml);
+ }
+
+ free(modlist);
+ free(dn);
+
+ if(ret!=0 && begin)
+ fprintf(output,"KEY %s FAILED %d\n",keyid,ret);
+
+ return ret;
+}
+
+static int
+send_key_keyserver(int *eof)
+{
+ int err,begin=0,end=0,keysize=1,ret=KEYSERVER_INTERNAL_ERROR;
char *dn=NULL,line[MAX_LINE],*key[2]={NULL,NULL};
char keyid[17];
LDAPMod mod, *attrs[2];
@@ -273,7 +773,7 @@ send_key(int *eof)
}
/* Note that key-not-found is not a fatal error */
-int
+static int
get_key(char *getkey)
{
LDAPMessage *res,*each;
@@ -507,51 +1007,7 @@ get_key(char *getkey)
return ret;
}
-time_t
-ldap2epochtime(const char *timestr)
-{
- struct tm pgptime;
- time_t answer;
-
- memset(&pgptime,0,sizeof(pgptime));
-
- /* YYYYMMDDHHmmssZ */
-
- sscanf(timestr,"%4d%2d%2d%2d%2d%2d",
- &pgptime.tm_year,
- &pgptime.tm_mon,
- &pgptime.tm_mday,
- &pgptime.tm_hour,
- &pgptime.tm_min,
- &pgptime.tm_sec);
-
- pgptime.tm_year-=1900;
- pgptime.tm_isdst=-1;
- pgptime.tm_mon--;
-
- /* mktime takes the timezone into account, and we can't have that.
- I'd use timegm, but it's not portable. */
-
-#ifdef HAVE_TIMEGM
- answer=timegm(&pgptime);
-#else
- {
- char *zone=getenv("TZ");
- setenv("TZ","UTC",1);
- tzset();
- answer=mktime(&pgptime);
- if(zone)
- setenv("TZ",zone,1);
- else
- unsetenv("TZ");
- tzset();
- }
-#endif
-
- return answer;
-}
-
-void
+static void
printquoted(FILE *stream,char *string,char delim)
{
while(*string)
@@ -567,7 +1023,7 @@ printquoted(FILE *stream,char *string,char delim)
/* Returns 0 on success and -1 on error. Note that key-not-found is
not an error! */
-int
+static int
search_key(char *searchkey)
{
char **vals;
@@ -799,7 +1255,7 @@ search_key(char *searchkey)
return KEYSERVER_OK;
}
-void
+static void
fail_all(struct keylist *keylist,int action,int err)
{
if(!keylist)
@@ -1331,8 +1787,16 @@ main(int argc,char *argv[])
do
{
- if(send_key(&eof)!=KEYSERVER_OK)
- failed++;
+ if(real_ldap)
+ {
+ if(send_key(&eof)!=KEYSERVER_OK)
+ failed++;
+ }
+ else
+ {
+ if(send_key_keyserver(&eof)!=KEYSERVER_OK)
+ failed++;
+ }
}
while(!eof);
}