1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-11-11 21:48:50 +01:00

* 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.
This commit is contained in:
David Shaw 2004-02-22 00:08:53 +00:00
parent 9afea90825
commit 3b9d7a6430
2 changed files with 534 additions and 57 deletions

View File

@ -1,3 +1,16 @@
2004-02-21 David Shaw <dshaw@jabberwocky.com>
* 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 <dshaw@jabberwocky.com> 2004-02-20 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c: Replacement prototypes for setenv and unsetenv. * gpgkeys_ldap.c: Replacement prototypes for setenv and unsetenv.

View File

@ -29,6 +29,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <errno.h> #include <errno.h>
#include <ldap.h> #include <ldap.h>
#include "util.h"
#include "keyserver.h" #include "keyserver.h"
#ifdef __riscos__ #ifdef __riscos__
@ -41,7 +42,7 @@ extern int optind;
#define GET 0 #define GET 0
#define SEND 1 #define SEND 1
#define SEARCH 2 #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 verbose=0,include_disabled=0,include_revoked=0,include_subkeys=0;
static int real_ldap=0; static int real_ldap=0;
@ -66,7 +67,7 @@ struct keylist
struct keylist *next; struct keylist *next;
}; };
int static int
ldap_err_to_gpg_err(int err) ldap_err_to_gpg_err(int err)
{ {
int ret; int ret;
@ -89,7 +90,7 @@ ldap_err_to_gpg_err(int err)
return ret; return ret;
} }
int static int
ldap_to_gpg_err(LDAP *ld) ldap_to_gpg_err(LDAP *ld)
{ {
#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_ERROR_NUMBER) #if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_ERROR_NUMBER)
@ -114,7 +115,7 @@ ldap_to_gpg_err(LDAP *ld)
#endif #endif
} }
int static int
key_in_keylist(const char *key,struct keylist *list) key_in_keylist(const char *key,struct keylist *list)
{ {
struct keylist *keyptr=list; struct keylist *keyptr=list;
@ -130,7 +131,7 @@ key_in_keylist(const char *key,struct keylist *list)
return 0; return 0;
} }
int static int
add_key_to_keylist(const char *key,struct keylist **list) add_key_to_keylist(const char *key,struct keylist **list)
{ {
struct keylist *keyptr=malloc(sizeof(struct keylist)); struct keylist *keyptr=malloc(sizeof(struct keylist));
@ -150,7 +151,7 @@ add_key_to_keylist(const char *key,struct keylist **list)
return 0; return 0;
} }
void static void
free_keylist(struct keylist *list) free_keylist(struct keylist *list)
{ {
while(list!=NULL) while(list!=NULL)
@ -162,8 +163,507 @@ 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) 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; int err,begin=0,end=0,keysize=1,ret=KEYSERVER_INTERNAL_ERROR;
char *dn=NULL,line[MAX_LINE],*key[2]={NULL,NULL}; char *dn=NULL,line[MAX_LINE],*key[2]={NULL,NULL};
@ -273,7 +773,7 @@ send_key(int *eof)
} }
/* Note that key-not-found is not a fatal error */ /* Note that key-not-found is not a fatal error */
int static int
get_key(char *getkey) get_key(char *getkey)
{ {
LDAPMessage *res,*each; LDAPMessage *res,*each;
@ -507,51 +1007,7 @@ get_key(char *getkey)
return ret; return ret;
} }
time_t static void
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
printquoted(FILE *stream,char *string,char delim) printquoted(FILE *stream,char *string,char delim)
{ {
while(*string) 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 /* Returns 0 on success and -1 on error. Note that key-not-found is
not an error! */ not an error! */
int static int
search_key(char *searchkey) search_key(char *searchkey)
{ {
char **vals; char **vals;
@ -799,7 +1255,7 @@ search_key(char *searchkey)
return KEYSERVER_OK; return KEYSERVER_OK;
} }
void static void
fail_all(struct keylist *keylist,int action,int err) fail_all(struct keylist *keylist,int action,int err)
{ {
if(!keylist) if(!keylist)
@ -1331,8 +1787,16 @@ main(int argc,char *argv[])
do do
{ {
if(send_key(&eof)!=KEYSERVER_OK) if(real_ldap)
failed++; {
if(send_key(&eof)!=KEYSERVER_OK)
failed++;
}
else
{
if(send_key_keyserver(&eof)!=KEYSERVER_OK)
failed++;
}
} }
while(!eof); while(!eof);
} }