2002-06-29 13:31:13 +00:00
|
|
|
/* gpgkeys_ldap.c - talk to a LDAP keyserver
|
2004-02-19 15:09:14 +00:00
|
|
|
* Copyright (C) 2001, 2002, 2004 Free Software Foundation, Inc.
|
2002-06-29 13:31:13 +00:00
|
|
|
*
|
|
|
|
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
2003-05-31 03:52:02 +00:00
|
|
|
#ifdef HAVE_GETOPT_H
|
|
|
|
#include <getopt.h>
|
|
|
|
#endif
|
2002-06-29 13:31:13 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <ldap.h>
|
2004-02-22 00:08:53 +00:00
|
|
|
#include "util.h"
|
2002-06-29 13:31:13 +00:00
|
|
|
#include "keyserver.h"
|
|
|
|
|
|
|
|
#ifdef __riscos__
|
2002-08-03 18:35:16 +00:00
|
|
|
#include "util.h"
|
2002-06-29 13:31:13 +00:00
|
|
|
#endif
|
|
|
|
|
2003-05-31 03:52:02 +00:00
|
|
|
extern char *optarg;
|
|
|
|
extern int optind;
|
|
|
|
|
2002-06-29 13:31:13 +00:00
|
|
|
#define GET 0
|
|
|
|
#define SEND 1
|
|
|
|
#define SEARCH 2
|
2004-02-22 00:08:53 +00:00
|
|
|
#define MAX_LINE 256
|
2002-06-29 13:31:13 +00:00
|
|
|
|
2004-02-19 20:09:12 +00:00
|
|
|
static int verbose=0,include_disabled=0,include_revoked=0,include_subkeys=0;
|
|
|
|
static int real_ldap=0;
|
|
|
|
static char *basekeyspacedn=NULL;
|
|
|
|
static char host[80]={'\0'};
|
|
|
|
static char portstr[10]={'\0'};
|
|
|
|
static char *pgpkeystr="pgpKey";
|
|
|
|
static FILE *input=NULL,*output=NULL,*console=NULL;
|
|
|
|
static LDAP *ldap=NULL;
|
2002-06-29 13:31:13 +00:00
|
|
|
|
2004-05-21 15:46:53 +00:00
|
|
|
#ifndef HAVE_TIMEGM
|
|
|
|
time_t timegm(struct tm *tm);
|
2004-02-20 14:59:02 +00:00
|
|
|
#endif
|
|
|
|
|
2002-06-29 13:31:13 +00:00
|
|
|
struct keylist
|
|
|
|
{
|
|
|
|
char str[MAX_LINE];
|
|
|
|
struct keylist *next;
|
|
|
|
};
|
|
|
|
|
2004-02-22 00:08:53 +00:00
|
|
|
static int
|
2002-09-24 20:17:52 +00:00
|
|
|
ldap_err_to_gpg_err(int err)
|
2002-06-29 13:31:13 +00:00
|
|
|
{
|
2002-09-24 20:17:52 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
switch(err)
|
|
|
|
{
|
|
|
|
case LDAP_ALREADY_EXISTS:
|
|
|
|
ret=KEYSERVER_KEY_EXISTS;
|
|
|
|
break;
|
|
|
|
|
2002-10-09 02:03:22 +00:00
|
|
|
case LDAP_SERVER_DOWN:
|
|
|
|
ret=KEYSERVER_UNREACHABLE;
|
|
|
|
break;
|
|
|
|
|
2002-09-24 20:17:52 +00:00
|
|
|
default:
|
|
|
|
ret=KEYSERVER_GENERAL_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2004-02-22 00:08:53 +00:00
|
|
|
static int
|
2002-09-24 20:17:52 +00:00
|
|
|
ldap_to_gpg_err(LDAP *ld)
|
|
|
|
{
|
2004-02-18 23:05:47 +00:00
|
|
|
#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_ERROR_NUMBER)
|
2002-09-24 20:17:52 +00:00
|
|
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if(ldap_get_option(ld,LDAP_OPT_ERROR_NUMBER,&err)==0)
|
|
|
|
return ldap_err_to_gpg_err(err);
|
|
|
|
else
|
|
|
|
return KEYSERVER_GENERAL_ERROR;
|
|
|
|
|
|
|
|
#elif defined(HAVE_LDAP_LD_ERRNO)
|
|
|
|
|
|
|
|
return ldap_err_to_gpg_err(ld->ld_errno);
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
/* We should never get here since the LDAP library should always
|
|
|
|
have either ldap_get_option or ld_errno, but just in case... */
|
|
|
|
return KEYSERVER_GENERAL_ERROR;
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2004-02-22 00:08:53 +00:00
|
|
|
static int
|
2002-11-05 22:08:02 +00:00
|
|
|
key_in_keylist(const char *key,struct keylist *list)
|
|
|
|
{
|
|
|
|
struct keylist *keyptr=list;
|
|
|
|
|
|
|
|
while(keyptr!=NULL)
|
|
|
|
{
|
|
|
|
if(strcasecmp(key,keyptr->str)==0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
keyptr=keyptr->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-02-22 00:08:53 +00:00
|
|
|
static int
|
2002-11-05 22:08:02 +00:00
|
|
|
add_key_to_keylist(const char *key,struct keylist **list)
|
|
|
|
{
|
|
|
|
struct keylist *keyptr=malloc(sizeof(struct keylist));
|
|
|
|
|
|
|
|
if(keyptr==NULL)
|
|
|
|
{
|
|
|
|
fprintf(console,"gpgkeys: out of memory when deduping "
|
|
|
|
"key list\n");
|
|
|
|
return KEYSERVER_NO_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
strncpy(keyptr->str,key,MAX_LINE);
|
|
|
|
keyptr->str[MAX_LINE-1]='\0';
|
|
|
|
keyptr->next=*list;
|
|
|
|
*list=keyptr;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-02-22 00:08:53 +00:00
|
|
|
static void
|
2002-11-05 22:08:02 +00:00
|
|
|
free_keylist(struct keylist *list)
|
|
|
|
{
|
|
|
|
while(list!=NULL)
|
|
|
|
{
|
|
|
|
struct keylist *keyptr=list;
|
|
|
|
|
|
|
|
list=keyptr->next;
|
|
|
|
free(keyptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-02-22 00:08:53 +00:00
|
|
|
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--;
|
|
|
|
|
2004-05-21 15:46:53 +00:00
|
|
|
/* mktime() takes the timezone into account, so we use timegm() */
|
2004-02-22 00:08:53 +00:00
|
|
|
|
|
|
|
answer=timegm(&pgptime);
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2004-02-24 03:57:21 +00:00
|
|
|
/* Passing a NULL for value effectively deletes that attribute. This
|
|
|
|
doesn't mean "delete" in the sense of removing something from the
|
|
|
|
modlist, but "delete" in the LDAP sense of adding a modlist item
|
|
|
|
that specifies LDAP_MOD_REPLACE and a null attribute for the given
|
|
|
|
attribute. LDAP_MOD_DELETE doesn't work here as we don't know if
|
|
|
|
the attribute in question exists or not. */
|
|
|
|
|
2004-02-22 00:08:53 +00:00
|
|
|
static int
|
2004-02-24 03:57:21 +00:00
|
|
|
make_one_attr(LDAPMod ***modlist,int unique,char *attr,const char *value)
|
2004-02-22 00:08:53 +00:00
|
|
|
{
|
|
|
|
LDAPMod **m;
|
|
|
|
int nummods=0;
|
|
|
|
|
|
|
|
/* Search modlist for the attribute we're playing with. */
|
|
|
|
for(m=*modlist;*m;m++)
|
|
|
|
{
|
2004-02-24 03:57:21 +00:00
|
|
|
if(strcasecmp((*m)->mod_type,attr)==0)
|
2004-02-22 00:08:53 +00:00
|
|
|
{
|
|
|
|
char **ptr=(*m)->mod_values;
|
|
|
|
int numvalues=0;
|
|
|
|
|
2004-02-24 03:57:21 +00:00
|
|
|
/* We have this attribute already, so when the REPLACE
|
|
|
|
happens, the server attributes will be replaced
|
|
|
|
anyway. */
|
|
|
|
if(!value)
|
|
|
|
return 1;
|
|
|
|
|
2004-02-22 00:08:53 +00:00
|
|
|
if(ptr)
|
2004-02-24 03:57:21 +00:00
|
|
|
for(ptr=(*m)->mod_values;*ptr;ptr++)
|
|
|
|
{
|
|
|
|
if(unique && strcmp(*ptr,value)==0)
|
|
|
|
return 1;
|
|
|
|
numvalues++;
|
|
|
|
}
|
2004-02-22 00:08:53 +00:00
|
|
|
|
|
|
|
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;
|
2004-02-24 03:57:21 +00:00
|
|
|
if(value)
|
2004-02-22 00:08:53 +00:00
|
|
|
{
|
2004-02-24 03:57:21 +00:00
|
|
|
grow[nummods]->mod_values=malloc(sizeof(char *)*2);
|
|
|
|
if(!grow[nummods]->mod_values)
|
|
|
|
{
|
|
|
|
grow[nummods]=NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
2004-02-22 00:08:53 +00:00
|
|
|
|
2004-02-24 03:57:21 +00:00
|
|
|
/* 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;
|
2004-02-22 00:08:53 +00:00
|
|
|
}
|
2004-02-24 03:57:21 +00:00
|
|
|
else
|
|
|
|
grow[nummods]->mod_values=NULL;
|
2004-02-22 00:08:53 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2004-02-24 03:57:21 +00:00
|
|
|
make_one_attr(modlist,0,"pgpCertID",tok);
|
|
|
|
make_one_attr(modlist,0,"pgpKeyID",&tok[8]);
|
2004-02-22 00:08:53 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* The primary pubkey algo */
|
|
|
|
if((tok=strsep(&line,":"))==NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch(atoi(tok))
|
|
|
|
{
|
|
|
|
case 1:
|
2004-02-24 03:57:21 +00:00
|
|
|
make_one_attr(modlist,0,"pgpKeyType","RSA");
|
2004-02-22 00:08:53 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 17:
|
2004-02-24 03:57:21 +00:00
|
|
|
make_one_attr(modlist,0,"pgpKeyType","DSS/DH");
|
2004-02-22 00:08:53 +00:00
|
|
|
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));
|
2004-02-24 03:57:21 +00:00
|
|
|
make_one_attr(modlist,0,"pgpKeySize",padded);
|
2004-02-22 00:08:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pk timestamp */
|
|
|
|
if((tok=strsep(&line,":"))==NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(atoi(tok)>0)
|
|
|
|
{
|
|
|
|
char *stamp=epoch2ldaptime(atoi(tok));
|
|
|
|
if(stamp)
|
|
|
|
{
|
2004-02-24 03:57:21 +00:00
|
|
|
make_one_attr(modlist,0,"pgpKeyCreateTime",stamp);
|
2004-02-22 00:08:53 +00:00
|
|
|
free(stamp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pk expire */
|
|
|
|
if((tok=strsep(&line,":"))==NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(atoi(tok)>0)
|
|
|
|
{
|
|
|
|
char *stamp=epoch2ldaptime(atoi(tok));
|
|
|
|
if(stamp)
|
|
|
|
{
|
2004-02-24 03:57:21 +00:00
|
|
|
make_one_attr(modlist,0,"pgpKeyExpireTime",stamp);
|
2004-02-22 00:08:53 +00:00
|
|
|
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))"
|
|
|
|
*/
|
|
|
|
|
2004-02-24 03:57:21 +00:00
|
|
|
make_one_attr(modlist,0,"pgpDisabled",disabled?"1":"0");
|
|
|
|
make_one_attr(modlist,0,"pgpRevoked",revoked?"1":"0");
|
2004-02-22 00:08:53 +00:00
|
|
|
}
|
|
|
|
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. */
|
|
|
|
|
2004-02-24 03:57:21 +00:00
|
|
|
make_one_attr(modlist,0,"pgpUserID",userid);
|
|
|
|
}
|
|
|
|
else if(ascii_strcasecmp("sig",record)==0)
|
|
|
|
{
|
|
|
|
char *tok;
|
|
|
|
|
|
|
|
if((tok=strsep(&line,":"))==NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(strlen(tok)==16)
|
|
|
|
make_one_attr(modlist,1,"pgpSignerID",tok);
|
2004-02-22 00:08:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
free_mod_values(LDAPMod *mod)
|
|
|
|
{
|
|
|
|
char **ptr;
|
|
|
|
|
|
|
|
if(!mod->mod_values)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for(ptr=mod->mod_values;*ptr;ptr++)
|
2004-02-24 03:57:21 +00:00
|
|
|
free(*ptr);
|
2004-02-22 00:08:53 +00:00
|
|
|
|
|
|
|
free(mod->mod_values);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-09-24 20:17:52 +00:00
|
|
|
send_key(int *eof)
|
2004-02-22 00:08:53 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2004-02-23 03:43:45 +00:00
|
|
|
/* Going on the assumption that modify operations are more frequent
|
|
|
|
than adds, I'm setting up the modify operations here first. */
|
2004-02-24 03:57:21 +00:00
|
|
|
make_one_attr(&modlist,0,"pgpDisabled",NULL);
|
|
|
|
make_one_attr(&modlist,0,"pgpKeyID",NULL);
|
|
|
|
make_one_attr(&modlist,0,"pgpKeyType",NULL);
|
|
|
|
make_one_attr(&modlist,0,"pgpUserID",NULL);
|
|
|
|
make_one_attr(&modlist,0,"pgpKeyCreateTime",NULL);
|
|
|
|
make_one_attr(&modlist,0,"pgpSignerID",NULL);
|
|
|
|
make_one_attr(&modlist,0,"pgpRevoked",NULL);
|
|
|
|
make_one_attr(&modlist,0,"pgpSubKeyID",NULL);
|
|
|
|
make_one_attr(&modlist,0,"pgpKeySize",NULL);
|
|
|
|
make_one_attr(&modlist,0,"pgpKeyExpireTime",NULL);
|
2004-02-26 01:29:26 +00:00
|
|
|
make_one_attr(&modlist,0,"pgpCertID",NULL);
|
|
|
|
/* Note the count of these deleted attributes. They're to be used
|
|
|
|
later. */
|
2004-02-23 03:43:45 +00:00
|
|
|
|
2004-02-22 00:08:53 +00:00
|
|
|
/* 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)
|
|
|
|
{
|
|
|
|
*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
|
2004-02-24 03:57:21 +00:00
|
|
|
build_attrs(&modlist,line);
|
2004-02-22 00:08:53 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2004-02-24 03:57:21 +00:00
|
|
|
make_one_attr(&modlist,0,"objectClass","pgpKeyInfo");
|
|
|
|
make_one_attr(&modlist,0,"pgpKey",key);
|
2004-02-22 00:08:53 +00:00
|
|
|
|
2004-02-23 03:43:45 +00:00
|
|
|
/* If it's not there, we just turn around and send an add command
|
|
|
|
for the same key. Otherwise, the modify brings the server copy
|
|
|
|
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. */
|
2004-02-22 00:08:53 +00:00
|
|
|
|
2004-02-23 03:43:45 +00:00
|
|
|
err=ldap_modify_s(ldap,dn,modlist);
|
|
|
|
if(err==LDAP_NO_SUCH_OBJECT)
|
|
|
|
{
|
2004-02-26 01:29:26 +00:00
|
|
|
/* This [11] is the deleted count from earlier */
|
|
|
|
LDAPMod **addlist=&modlist[11];
|
2004-02-23 03:43:45 +00:00
|
|
|
err=ldap_add_s(ldap,dn,addlist);
|
|
|
|
}
|
2004-02-22 00:08:53 +00:00
|
|
|
|
|
|
|
if(err!=LDAP_SUCCESS)
|
|
|
|
{
|
|
|
|
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)
|
2002-09-24 20:17:52 +00:00
|
|
|
{
|
2002-11-04 13:59:08 +00:00
|
|
|
int err,begin=0,end=0,keysize=1,ret=KEYSERVER_INTERNAL_ERROR;
|
2002-10-14 19:02:11 +00:00
|
|
|
char *dn=NULL,line[MAX_LINE],*key[2]={NULL,NULL};
|
2002-06-29 13:31:13 +00:00
|
|
|
char keyid[17];
|
|
|
|
LDAPMod mod, *attrs[2];
|
2002-09-27 19:33:52 +00:00
|
|
|
|
2004-02-26 01:29:26 +00:00
|
|
|
memset(&mod,0,sizeof(mod));
|
|
|
|
mod.mod_op=LDAP_MOD_ADD;
|
|
|
|
mod.mod_type=pgpkeystr;
|
|
|
|
mod.mod_values=key;
|
|
|
|
attrs[0]=&mod;
|
|
|
|
attrs[1]=NULL;
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
dn=malloc(strlen("pgpCertid=virtual,")+strlen(basekeyspacedn)+1);
|
|
|
|
if(dn==NULL)
|
|
|
|
{
|
|
|
|
fprintf(console,"gpgkeys: can't allocate memory for keyserver record\n");
|
2002-09-24 20:17:52 +00:00
|
|
|
ret=KEYSERVER_NO_MEMORY;
|
2002-06-29 13:31:13 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(dn,"pgpCertid=virtual,");
|
|
|
|
strcat(dn,basekeyspacedn);
|
|
|
|
|
|
|
|
key[0]=malloc(1);
|
|
|
|
if(key[0]==NULL)
|
|
|
|
{
|
|
|
|
fprintf(console,"gpgkeys: unable to allocate memory for key\n");
|
2002-09-24 20:17:52 +00:00
|
|
|
ret=KEYSERVER_NO_MEMORY;
|
2002-06-29 13:31:13 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
key[0][0]='\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)
|
|
|
|
{
|
2002-11-04 13:59:08 +00:00
|
|
|
begin=1;
|
2002-06-29 13:31:13 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2002-11-04 13:59:08 +00:00
|
|
|
if(!begin)
|
2002-06-29 13:31:13 +00:00
|
|
|
{
|
2002-09-24 20:17:52 +00:00
|
|
|
/* i.e. eof before the KEY BEGIN was found. This isn't an
|
|
|
|
error. */
|
|
|
|
*eof=1;
|
|
|
|
ret=KEYSERVER_OK;
|
2002-06-29 13:31:13 +00:00
|
|
|
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)
|
|
|
|
{
|
2002-11-04 13:59:08 +00:00
|
|
|
end=1;
|
2002-06-29 13:31:13 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
keysize+=strlen(line);
|
|
|
|
key[0]=realloc(key[0],keysize);
|
|
|
|
if(key[0]==NULL)
|
|
|
|
{
|
|
|
|
fprintf(console,"gpgkeys: unable to reallocate for key\n");
|
2002-09-24 20:17:52 +00:00
|
|
|
ret=KEYSERVER_NO_MEMORY;
|
2002-06-29 13:31:13 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcat(key[0],line);
|
|
|
|
}
|
|
|
|
|
2002-11-04 13:59:08 +00:00
|
|
|
if(!end)
|
2002-06-29 13:31:13 +00:00
|
|
|
{
|
|
|
|
fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
|
2002-09-24 20:17:52 +00:00
|
|
|
*eof=1;
|
|
|
|
ret=KEYSERVER_KEY_INCOMPLETE;
|
2002-06-29 13:31:13 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
err=ldap_add_s(ldap,dn,attrs);
|
|
|
|
if(err!=LDAP_SUCCESS)
|
|
|
|
{
|
|
|
|
fprintf(console,"gpgkeys: error adding key %s to keyserver: %s\n",
|
|
|
|
keyid,ldap_err2string(err));
|
2002-09-24 20:17:52 +00:00
|
|
|
ret=ldap_err_to_gpg_err(err);
|
2002-06-29 13:31:13 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2002-09-24 20:17:52 +00:00
|
|
|
ret=KEYSERVER_OK;
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
fail:
|
|
|
|
|
|
|
|
free(key[0]);
|
|
|
|
free(dn);
|
|
|
|
|
2002-11-04 13:59:08 +00:00
|
|
|
if(ret!=0 && begin)
|
2002-09-24 20:17:52 +00:00
|
|
|
fprintf(output,"KEY %s FAILED %d\n",keyid,ret);
|
|
|
|
|
|
|
|
/* Not a fatal error */
|
|
|
|
if(ret==KEYSERVER_KEY_EXISTS)
|
|
|
|
ret=KEYSERVER_OK;
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2002-09-24 20:17:52 +00:00
|
|
|
/* Note that key-not-found is not a fatal error */
|
2004-02-22 00:08:53 +00:00
|
|
|
static int
|
2002-09-24 20:17:52 +00:00
|
|
|
get_key(char *getkey)
|
2002-06-29 13:31:13 +00:00
|
|
|
{
|
|
|
|
LDAPMessage *res,*each;
|
2002-09-24 20:17:52 +00:00
|
|
|
int ret=KEYSERVER_INTERNAL_ERROR,err,count;
|
2002-06-29 13:31:13 +00:00
|
|
|
struct keylist *dupelist=NULL;
|
|
|
|
char search[62];
|
2002-11-14 14:30:53 +00:00
|
|
|
/* This ordering is significant - specifically, "pgpcertid" needs to
|
|
|
|
be the second item in the list, since everything after it may be
|
|
|
|
discarded if the user isn't in verbose mode. */
|
|
|
|
char *attrs[]={"replaceme","pgpcertid","pgpuserid","pgpkeyid","pgprevoked",
|
2002-07-15 23:13:48 +00:00
|
|
|
"pgpdisabled","pgpkeycreatetime","modifytimestamp",
|
|
|
|
"pgpkeysize","pgpkeytype",NULL};
|
2002-09-09 20:36:12 +00:00
|
|
|
attrs[0]=pgpkeystr; /* Some compilers don't like using variables as
|
|
|
|
array initializers. */
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
/* Build the search string */
|
|
|
|
|
|
|
|
/* GPG can send us a v4 fingerprint, a v3 or v4 long key id, or a v3
|
|
|
|
or v4 short key id */
|
|
|
|
|
|
|
|
if(strncmp(getkey,"0x",2)==0)
|
|
|
|
getkey+=2;
|
|
|
|
|
|
|
|
if(strlen(getkey)==32)
|
|
|
|
{
|
|
|
|
fprintf(console,
|
|
|
|
"gpgkeys: LDAP keyservers do not support v3 fingerprints\n");
|
|
|
|
fprintf(output,"KEY 0x%s BEGIN\n",getkey);
|
2002-09-24 20:17:52 +00:00
|
|
|
fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_NOT_SUPPORTED);
|
|
|
|
return KEYSERVER_NOT_SUPPORTED;
|
2002-06-29 13:31:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(strlen(getkey)>16)
|
|
|
|
{
|
|
|
|
char *offset=&getkey[strlen(getkey)-16];
|
|
|
|
|
|
|
|
/* fingerprint. Take the last 16 characters and treat it like a
|
|
|
|
long key id */
|
|
|
|
|
|
|
|
if(include_subkeys)
|
|
|
|
sprintf(search,"(|(pgpcertid=%.16s)(pgpsubkeyid=%.16s))",
|
|
|
|
offset,offset);
|
|
|
|
else
|
|
|
|
sprintf(search,"(pgpcertid=%.16s)",offset);
|
|
|
|
}
|
|
|
|
else if(strlen(getkey)>8)
|
|
|
|
{
|
|
|
|
/* long key id */
|
|
|
|
|
|
|
|
if(include_subkeys)
|
|
|
|
sprintf(search,"(|(pgpcertid=%.16s)(pgpsubkeyid=%.16s))",
|
|
|
|
getkey,getkey);
|
|
|
|
else
|
|
|
|
sprintf(search,"(pgpcertid=%.16s)",getkey);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* short key id */
|
|
|
|
|
|
|
|
sprintf(search,"(pgpkeyid=%.8s)",getkey);
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(output,"KEY 0x%s BEGIN\n",getkey);
|
|
|
|
|
|
|
|
if(verbose>2)
|
|
|
|
fprintf(console,"gpgkeys: LDAP fetch for: %s\n",search);
|
|
|
|
|
|
|
|
if(!verbose)
|
2002-11-14 14:30:53 +00:00
|
|
|
attrs[2]=NULL; /* keep only pgpkey(v2) and pgpcertid */
|
2002-06-29 13:31:13 +00:00
|
|
|
|
2002-07-04 14:14:08 +00:00
|
|
|
if(verbose)
|
|
|
|
fprintf(console,"gpgkeys: requesting key 0x%s from ldap://%s%s%s\n",
|
2002-08-19 21:14:57 +00:00
|
|
|
getkey,host,portstr[0]?":":"",portstr[0]?portstr:"");
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
err=ldap_search_s(ldap,basekeyspacedn,
|
|
|
|
LDAP_SCOPE_SUBTREE,search,attrs,0,&res);
|
|
|
|
if(err!=0)
|
|
|
|
{
|
2002-09-24 20:17:52 +00:00
|
|
|
int errtag=ldap_err_to_gpg_err(err);
|
|
|
|
|
2002-06-29 13:31:13 +00:00
|
|
|
fprintf(console,"gpgkeys: LDAP search error: %s\n",ldap_err2string(err));
|
2002-09-24 20:17:52 +00:00
|
|
|
fprintf(output,"KEY 0x%s FAILED %d\n",getkey,errtag);
|
|
|
|
return errtag;
|
2002-06-29 13:31:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
count=ldap_count_entries(ldap,res);
|
|
|
|
if(count<1)
|
|
|
|
{
|
|
|
|
fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
|
2002-09-24 20:17:52 +00:00
|
|
|
fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_KEY_NOT_FOUND);
|
2002-06-29 13:31:13 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* There may be more than one unique result for a given keyID,
|
|
|
|
so we should fetch them all (test this by fetching short key
|
|
|
|
id 0xDEADBEEF). */
|
|
|
|
|
|
|
|
each=ldap_first_entry(ldap,res);
|
|
|
|
while(each!=NULL)
|
|
|
|
{
|
2002-11-05 22:08:02 +00:00
|
|
|
char **vals,**certid;
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
/* Use the long keyid to remove duplicates. The LDAP server
|
|
|
|
returns the same keyid more than once if there are
|
|
|
|
multiple user IDs on the key. Note that this does NOT
|
|
|
|
mean that a keyid that exists multiple times on the
|
|
|
|
keyserver will not be fetched. It means that each KEY,
|
2002-11-05 22:08:02 +00:00
|
|
|
no matter how many user IDs share its keyid, will be
|
2002-06-29 13:31:13 +00:00
|
|
|
fetched only once. If a keyid that belongs to more than
|
|
|
|
one key is fetched, the server quite properly responds
|
|
|
|
with all matching keys. -ds */
|
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
certid=ldap_get_values(ldap,each,"pgpcertid");
|
|
|
|
if(certid!=NULL)
|
2002-06-29 13:31:13 +00:00
|
|
|
{
|
2002-11-05 22:08:02 +00:00
|
|
|
if(!key_in_keylist(certid[0],dupelist))
|
2002-06-29 13:31:13 +00:00
|
|
|
{
|
|
|
|
/* it's not a duplicate, so add it */
|
|
|
|
|
2002-11-10 21:32:11 +00:00
|
|
|
int rc=add_key_to_keylist(certid[0],&dupelist);
|
2002-11-05 22:08:02 +00:00
|
|
|
if(rc)
|
2002-06-29 13:31:13 +00:00
|
|
|
{
|
2002-11-05 22:08:02 +00:00
|
|
|
ret=rc;
|
2002-06-29 13:31:13 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
if(verbose)
|
2002-06-29 13:31:13 +00:00
|
|
|
{
|
2002-11-05 22:08:02 +00:00
|
|
|
vals=ldap_get_values(ldap,each,"pgpuserid");
|
|
|
|
if(vals!=NULL)
|
|
|
|
{
|
|
|
|
/* This is wrong, as the user ID is UTF8. A
|
|
|
|
better way to handle this would be to send it
|
|
|
|
over to gpg and display it on that side of
|
|
|
|
the pipe. */
|
|
|
|
fprintf(console,"\nUser ID:\t%s\n",vals[0]);
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
|
|
|
|
|
|
|
vals=ldap_get_values(ldap,each,"pgprevoked");
|
|
|
|
if(vals!=NULL)
|
|
|
|
{
|
|
|
|
if(atoi(vals[0])==1)
|
|
|
|
fprintf(console,"\t\t** KEY REVOKED **\n");
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
|
|
|
|
|
|
|
vals=ldap_get_values(ldap,each,"pgpdisabled");
|
|
|
|
if(vals!=NULL)
|
|
|
|
{
|
|
|
|
if(atoi(vals[0])==1)
|
|
|
|
fprintf(console,"\t\t** KEY DISABLED **\n");
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
|
|
|
|
|
|
|
vals=ldap_get_values(ldap,each,"pgpkeyid");
|
|
|
|
if(vals!=NULL)
|
|
|
|
{
|
|
|
|
fprintf(console,"Short key ID:\t%s\n",vals[0]);
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(console,"Long key ID:\t%s\n",certid[0]);
|
|
|
|
|
|
|
|
/* YYYYMMDDHHmmssZ */
|
|
|
|
|
|
|
|
vals=ldap_get_values(ldap,each,"pgpkeycreatetime");
|
|
|
|
if(vals!=NULL)
|
|
|
|
{
|
|
|
|
if(strlen(vals[0])==15)
|
|
|
|
fprintf(console,"Key created:\t%.2s/%.2s/%.4s\n",
|
|
|
|
&vals[0][4],&vals[0][6],vals[0]);
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
|
|
|
|
|
|
|
vals=ldap_get_values(ldap,each,"modifytimestamp");
|
|
|
|
if(vals!=NULL)
|
|
|
|
{
|
|
|
|
if(strlen(vals[0])==15)
|
|
|
|
fprintf(console,"Key modified:\t%.2s/%.2s/%.4s\n",
|
|
|
|
&vals[0][4],&vals[0][6],vals[0]);
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
|
|
|
|
|
|
|
vals=ldap_get_values(ldap,each,"pgpkeysize");
|
|
|
|
if(vals!=NULL)
|
|
|
|
{
|
|
|
|
if(atoi(vals[0])>0)
|
|
|
|
fprintf(console,"Key size:\t%d\n",atoi(vals[0]));
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
|
|
|
|
|
|
|
vals=ldap_get_values(ldap,each,"pgpkeytype");
|
|
|
|
if(vals!=NULL)
|
|
|
|
{
|
|
|
|
fprintf(console,"Key type:\t%s\n",vals[0]);
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
2002-06-29 13:31:13 +00:00
|
|
|
}
|
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
vals=ldap_get_values(ldap,each,pgpkeystr);
|
|
|
|
if(vals==NULL)
|
2002-06-29 13:31:13 +00:00
|
|
|
{
|
2002-11-05 22:08:02 +00:00
|
|
|
int errtag=ldap_to_gpg_err(ldap);
|
2002-06-29 13:31:13 +00:00
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
fprintf(console,"gpgkeys: unable to retrieve key %s "
|
|
|
|
"from keyserver\n",getkey);
|
|
|
|
fprintf(output,"KEY 0x%s FAILED %d\n",getkey,errtag);
|
2002-06-29 13:31:13 +00:00
|
|
|
}
|
2002-11-05 22:08:02 +00:00
|
|
|
else
|
2002-06-29 13:31:13 +00:00
|
|
|
{
|
2002-11-05 22:08:02 +00:00
|
|
|
fprintf(output,"%sKEY 0x%s END\n",vals[0],getkey);
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
ldap_value_free(certid);
|
2002-06-29 13:31:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
each=ldap_next_entry(ldap,each);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-09-24 20:17:52 +00:00
|
|
|
ret=KEYSERVER_OK;
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
fail:
|
|
|
|
ldap_msgfree(res);
|
2002-11-05 22:08:02 +00:00
|
|
|
free_keylist(dupelist);
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2004-02-22 00:08:53 +00:00
|
|
|
static void
|
2002-10-14 19:02:11 +00:00
|
|
|
printquoted(FILE *stream,char *string,char delim)
|
2002-06-29 13:31:13 +00:00
|
|
|
{
|
|
|
|
while(*string)
|
|
|
|
{
|
2002-10-14 19:02:11 +00:00
|
|
|
if(*string==delim || *string=='%')
|
|
|
|
fprintf(stream,"%%%02x",*string);
|
2002-06-29 13:31:13 +00:00
|
|
|
else
|
|
|
|
fputc(*string,stream);
|
|
|
|
|
|
|
|
string++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns 0 on success and -1 on error. Note that key-not-found is
|
|
|
|
not an error! */
|
2004-02-22 00:08:53 +00:00
|
|
|
static int
|
2002-09-24 20:17:52 +00:00
|
|
|
search_key(char *searchkey)
|
2002-06-29 13:31:13 +00:00
|
|
|
{
|
|
|
|
char **vals;
|
|
|
|
LDAPMessage *res,*each;
|
2002-11-05 22:08:02 +00:00
|
|
|
int err,count=0;
|
|
|
|
struct keylist *dupelist=NULL;
|
2004-01-12 04:09:37 +00:00
|
|
|
/* The maximum size of the search, including the optional stuff and
|
2002-06-29 13:31:13 +00:00
|
|
|
the trailing \0 */
|
|
|
|
char search[2+12+MAX_LINE+2+15+14+1+1];
|
|
|
|
char *attrs[]={"pgpcertid","pgpuserid","pgprevoked","pgpdisabled",
|
|
|
|
"pgpkeycreatetime","pgpkeyexpiretime","modifytimestamp",
|
|
|
|
"pgpkeysize","pgpkeytype",NULL};
|
|
|
|
|
|
|
|
fprintf(output,"SEARCH %s BEGIN\n",searchkey);
|
|
|
|
|
|
|
|
/* Build the search string */
|
|
|
|
|
|
|
|
sprintf(search,"%s(pgpuserid=*%s*)%s%s%s",
|
|
|
|
(!(include_disabled&&include_revoked))?"(&":"",
|
|
|
|
searchkey,
|
|
|
|
include_disabled?"":"(pgpdisabled=0)",
|
|
|
|
include_revoked?"":"(pgprevoked=0)",
|
|
|
|
!(include_disabled&&include_revoked)?")":"");
|
|
|
|
|
|
|
|
if(verbose>2)
|
|
|
|
fprintf(console,"gpgkeys: LDAP search for: %s\n",search);
|
|
|
|
|
|
|
|
fprintf(console,("gpgkeys: searching for \"%s\" from LDAP server %s\n"),
|
|
|
|
searchkey,host);
|
|
|
|
|
|
|
|
err=ldap_search_s(ldap,basekeyspacedn,
|
|
|
|
LDAP_SCOPE_SUBTREE,search,attrs,0,&res);
|
2004-02-20 14:59:02 +00:00
|
|
|
if(err!=LDAP_SUCCESS && err!=LDAP_SIZELIMIT_EXCEEDED)
|
2002-06-29 13:31:13 +00:00
|
|
|
{
|
2002-09-24 20:17:52 +00:00
|
|
|
int errtag=ldap_err_to_gpg_err(err);
|
|
|
|
|
|
|
|
fprintf(output,"SEARCH %s FAILED %d\n",searchkey,errtag);
|
2002-06-29 13:31:13 +00:00
|
|
|
fprintf(console,"gpgkeys: LDAP search error: %s\n",ldap_err2string(err));
|
2002-09-24 20:17:52 +00:00
|
|
|
return errtag;
|
2002-06-29 13:31:13 +00:00
|
|
|
}
|
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
/* The LDAP server doesn't return a real count of unique keys, so we
|
|
|
|
can't use ldap_count_entries here. */
|
|
|
|
each=ldap_first_entry(ldap,res);
|
|
|
|
while(each!=NULL)
|
|
|
|
{
|
|
|
|
char **certid=ldap_get_values(ldap,each,"pgpcertid");
|
|
|
|
|
|
|
|
if(certid!=NULL)
|
|
|
|
{
|
|
|
|
if(!key_in_keylist(certid[0],dupelist))
|
|
|
|
{
|
|
|
|
int rc=add_key_to_keylist(certid[0],&dupelist);
|
|
|
|
if(rc!=0)
|
|
|
|
{
|
|
|
|
fprintf(output,"SEARCH %s FAILED %d\n",searchkey,rc);
|
|
|
|
free_keylist(dupelist);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
each=ldap_next_entry(ldap,each);
|
|
|
|
}
|
|
|
|
|
2004-02-20 14:59:02 +00:00
|
|
|
if(err==LDAP_SIZELIMIT_EXCEEDED)
|
|
|
|
fprintf(console,"gpgkeys: search results exceeded server limit. First %d results shown.\n",count);
|
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
free_keylist(dupelist);
|
|
|
|
dupelist=NULL;
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
if(count<1)
|
2002-10-14 19:02:11 +00:00
|
|
|
fprintf(output,"info:1:0\n");
|
2002-06-29 13:31:13 +00:00
|
|
|
else
|
|
|
|
{
|
2002-10-14 19:02:11 +00:00
|
|
|
fprintf(output,"info:1:%d\n",count);
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
each=ldap_first_entry(ldap,res);
|
|
|
|
while(each!=NULL)
|
|
|
|
{
|
2002-11-05 22:08:02 +00:00
|
|
|
char **certid;
|
2002-06-29 13:31:13 +00:00
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
certid=ldap_get_values(ldap,each,"pgpcertid");
|
|
|
|
if(certid!=NULL)
|
2002-06-29 13:31:13 +00:00
|
|
|
{
|
2002-11-05 22:08:02 +00:00
|
|
|
LDAPMessage *uids;
|
2002-06-29 13:31:13 +00:00
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
/* Have we seen this certid before? */
|
|
|
|
if(!key_in_keylist(certid[0],dupelist))
|
|
|
|
{
|
|
|
|
int rc=add_key_to_keylist(certid[0],&dupelist);
|
|
|
|
if(rc)
|
|
|
|
{
|
|
|
|
fprintf(output,"SEARCH %s FAILED %d\n",searchkey,rc);
|
|
|
|
free_keylist(dupelist);
|
|
|
|
ldap_value_free(certid);
|
|
|
|
ldap_msgfree(res);
|
|
|
|
return rc;
|
|
|
|
}
|
2002-06-29 13:31:13 +00:00
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
fprintf(output,"pub:%s:",certid[0]);
|
2002-06-29 13:31:13 +00:00
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
vals=ldap_get_values(ldap,each,"pgpkeytype");
|
|
|
|
if(vals!=NULL)
|
|
|
|
{
|
|
|
|
/* The LDAP server doesn't exactly handle this
|
|
|
|
well. */
|
|
|
|
if(strcasecmp(vals[0],"RSA")==0)
|
|
|
|
fprintf(output,"1");
|
|
|
|
else if(strcasecmp(vals[0],"DSS/DH")==0)
|
|
|
|
fprintf(output,"17");
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
2002-10-14 19:02:11 +00:00
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
fputc(':',output);
|
|
|
|
|
|
|
|
vals=ldap_get_values(ldap,each,"pgpkeysize");
|
|
|
|
if(vals!=NULL)
|
|
|
|
{
|
|
|
|
/* Not sure why, but some keys are listed with a
|
|
|
|
key size of 0. Treat that like an
|
|
|
|
unknown. */
|
|
|
|
if(atoi(vals[0])>0)
|
|
|
|
fprintf(output,"%d",atoi(vals[0]));
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
2002-06-29 13:31:13 +00:00
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
fputc(':',output);
|
2002-06-29 13:31:13 +00:00
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
/* YYYYMMDDHHmmssZ */
|
2002-06-29 13:31:13 +00:00
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
vals=ldap_get_values(ldap,each,"pgpkeycreatetime");
|
|
|
|
if(vals!=NULL && strlen(vals[0])==15)
|
|
|
|
{
|
|
|
|
fprintf(output,"%u",
|
|
|
|
(unsigned int)ldap2epochtime(vals[0]));
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
2002-10-14 19:02:11 +00:00
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
fputc(':',output);
|
2002-06-29 13:31:13 +00:00
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
vals=ldap_get_values(ldap,each,"pgpkeyexpiretime");
|
|
|
|
if(vals!=NULL && strlen(vals[0])==15)
|
|
|
|
{
|
|
|
|
fprintf(output,"%u",
|
|
|
|
(unsigned int)ldap2epochtime(vals[0]));
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
2002-06-29 13:31:13 +00:00
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
fputc(':',output);
|
2002-10-14 19:02:11 +00:00
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
vals=ldap_get_values(ldap,each,"pgprevoked");
|
|
|
|
if(vals!=NULL)
|
|
|
|
{
|
|
|
|
if(atoi(vals[0])==1)
|
|
|
|
fprintf(output,"r");
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
2002-06-29 13:31:13 +00:00
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
vals=ldap_get_values(ldap,each,"pgpdisabled");
|
|
|
|
if(vals!=NULL)
|
|
|
|
{
|
|
|
|
if(atoi(vals[0])==1)
|
|
|
|
fprintf(output,"d");
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
2002-06-29 13:31:13 +00:00
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
#if 0
|
|
|
|
/* This is not yet specified in the keyserver
|
|
|
|
protocol, but may be someday. */
|
|
|
|
fputc(':',output);
|
2002-10-14 19:02:11 +00:00
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
vals=ldap_get_values(ldap,each,"modifytimestamp");
|
|
|
|
if(vals!=NULL && strlen(vals[0])==15)
|
|
|
|
{
|
|
|
|
fprintf(output,"%u",
|
|
|
|
(unsigned int)ldap2epochtime(vals[0]));
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
|
|
|
#endif
|
2002-10-14 19:02:11 +00:00
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
fprintf(output,"\n");
|
2002-10-14 19:02:11 +00:00
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
/* Now print all the uids that have this certid */
|
|
|
|
uids=ldap_first_entry(ldap,res);
|
|
|
|
while(uids!=NULL)
|
|
|
|
{
|
|
|
|
vals=ldap_get_values(ldap,uids,"pgpcertid");
|
|
|
|
if(vals!=NULL)
|
|
|
|
{
|
|
|
|
if(strcasecmp(certid[0],vals[0])==0)
|
|
|
|
{
|
|
|
|
char **uidvals;
|
|
|
|
|
|
|
|
fprintf(output,"uid:");
|
|
|
|
|
|
|
|
uidvals=ldap_get_values(ldap,uids,"pgpuserid");
|
|
|
|
if(uidvals!=NULL)
|
|
|
|
{
|
|
|
|
/* Need to escape any colons */
|
|
|
|
printquoted(output,uidvals[0],':');
|
|
|
|
ldap_value_free(uidvals);
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(output,"\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
|
|
|
|
|
|
|
uids=ldap_next_entry(ldap,uids);
|
|
|
|
}
|
|
|
|
}
|
2002-06-29 13:31:13 +00:00
|
|
|
|
2002-11-05 22:08:02 +00:00
|
|
|
ldap_value_free(certid);
|
|
|
|
}
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
each=ldap_next_entry(ldap,each);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ldap_msgfree(res);
|
2002-11-05 22:08:02 +00:00
|
|
|
free_keylist(dupelist);
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
fprintf(output,"SEARCH %s END\n",searchkey);
|
|
|
|
|
2002-09-24 20:17:52 +00:00
|
|
|
return KEYSERVER_OK;
|
2002-06-29 13:31:13 +00:00
|
|
|
}
|
|
|
|
|
2004-02-22 00:08:53 +00:00
|
|
|
static void
|
2002-10-09 02:03:22 +00:00
|
|
|
fail_all(struct keylist *keylist,int action,int err)
|
|
|
|
{
|
|
|
|
if(!keylist)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(action==SEARCH)
|
|
|
|
{
|
|
|
|
fprintf(output,"SEARCH ");
|
|
|
|
while(keylist)
|
|
|
|
{
|
|
|
|
fprintf(output,"%s ",keylist->str);
|
|
|
|
keylist=keylist->next;
|
|
|
|
}
|
|
|
|
fprintf(output,"FAILED %d\n",err);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
while(keylist)
|
|
|
|
{
|
|
|
|
fprintf(output,"KEY %s FAILED %d\n",keylist->str,err);
|
|
|
|
keylist=keylist->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-02-19 15:09:14 +00:00
|
|
|
static int
|
|
|
|
find_basekeyspacedn(void)
|
|
|
|
{
|
|
|
|
int err,i;
|
|
|
|
char *attr[]={"namingContexts",NULL,NULL,NULL};
|
|
|
|
LDAPMessage *res;
|
|
|
|
char **context;
|
|
|
|
|
|
|
|
/* Look for namingContexts */
|
|
|
|
err=ldap_search_s(ldap,"",LDAP_SCOPE_BASE,"(objectClass=*)",attr,0,&res);
|
|
|
|
if(err==LDAP_SUCCESS)
|
|
|
|
{
|
|
|
|
context=ldap_get_values(ldap,res,"namingContexts");
|
2004-02-20 14:59:02 +00:00
|
|
|
if(context)
|
2004-02-19 15:09:14 +00:00
|
|
|
{
|
2004-02-20 14:59:02 +00:00
|
|
|
attr[0]="pgpBaseKeySpaceDN";
|
|
|
|
attr[1]="pgpVersion";
|
|
|
|
attr[2]="pgpSoftware";
|
2004-02-19 15:09:14 +00:00
|
|
|
|
2004-02-20 14:59:02 +00:00
|
|
|
real_ldap=1;
|
2004-02-19 15:09:14 +00:00
|
|
|
|
2004-02-20 14:59:02 +00:00
|
|
|
/* We found some, so try each namingContext as the search base
|
|
|
|
and look for pgpBaseKeySpaceDN. Because we found this, we
|
|
|
|
know we're talking to a regular-ish LDAP server and not a
|
|
|
|
LDAP keyserver. */
|
|
|
|
|
|
|
|
for(i=0;context[i] && !basekeyspacedn;i++)
|
2004-02-19 15:09:14 +00:00
|
|
|
{
|
2004-02-20 14:59:02 +00:00
|
|
|
char **vals;
|
|
|
|
LDAPMessage *si_res;
|
|
|
|
err=ldap_search_s(ldap,context[i],LDAP_SCOPE_ONELEVEL,
|
|
|
|
"(cn=pgpServerInfo)",attr,0,&si_res);
|
|
|
|
if(err!=LDAP_SUCCESS)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
vals=ldap_get_values(ldap,si_res,"pgpBaseKeySpaceDN");
|
2004-02-19 15:09:14 +00:00
|
|
|
if(vals)
|
|
|
|
{
|
2004-02-20 14:59:02 +00:00
|
|
|
/* This is always "OU=ACTIVE,O=PGP KEYSPACE,C=US", but
|
|
|
|
it might not be in the future. */
|
|
|
|
|
|
|
|
basekeyspacedn=strdup(vals[0]);
|
2004-02-19 15:09:14 +00:00
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
|
|
|
|
2004-02-20 14:59:02 +00:00
|
|
|
if(verbose>1)
|
2004-02-19 15:09:14 +00:00
|
|
|
{
|
2004-02-20 14:59:02 +00:00
|
|
|
vals=ldap_get_values(ldap,si_res,"pgpSoftware");
|
|
|
|
if(vals)
|
|
|
|
{
|
|
|
|
fprintf(console,"Server: \t%s\n",vals[0]);
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
|
|
|
|
|
|
|
vals=ldap_get_values(ldap,si_res,"pgpVersion");
|
|
|
|
if(vals)
|
|
|
|
{
|
|
|
|
fprintf(console,"Version:\t%s\n",vals[0]);
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
2004-02-19 15:09:14 +00:00
|
|
|
}
|
2004-02-20 14:59:02 +00:00
|
|
|
|
|
|
|
ldap_msgfree(si_res);
|
2004-02-19 15:09:14 +00:00
|
|
|
}
|
|
|
|
|
2004-02-20 14:59:02 +00:00
|
|
|
ldap_value_free(context);
|
2004-02-19 15:09:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ldap_msgfree(res);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* We don't have an answer yet, which means the server might be
|
|
|
|
a LDAP keyserver. */
|
|
|
|
char **vals;
|
|
|
|
LDAPMessage *si_res;
|
|
|
|
|
|
|
|
attr[0]="pgpBaseKeySpaceDN";
|
|
|
|
attr[1]="version";
|
|
|
|
attr[2]="software";
|
|
|
|
|
|
|
|
err=ldap_search_s(ldap,"cn=pgpServerInfo",LDAP_SCOPE_BASE,
|
|
|
|
"(objectClass=*)",attr,0,&si_res);
|
|
|
|
if(err!=LDAP_SUCCESS)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
vals=ldap_get_values(ldap,si_res,"baseKeySpaceDN");
|
|
|
|
if(vals)
|
|
|
|
{
|
|
|
|
basekeyspacedn=strdup(vals[0]);
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(verbose>1)
|
|
|
|
{
|
|
|
|
vals=ldap_get_values(ldap,si_res,"software");
|
|
|
|
if(vals)
|
|
|
|
{
|
|
|
|
fprintf(console,"Server: \t%s\n",vals[0]);
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
vals=ldap_get_values(ldap,si_res,"version");
|
|
|
|
if(vals)
|
|
|
|
{
|
|
|
|
if(verbose>1)
|
|
|
|
fprintf(console,"Version:\t%s\n",vals[0]);
|
|
|
|
|
|
|
|
/* If the version is high enough, use the new pgpKeyV2
|
|
|
|
attribute. This design if iffy at best, but it matches how
|
|
|
|
PGP does it. I figure the NAI folks assumed that there would
|
|
|
|
never be a LDAP keyserver vendor with a different numbering
|
|
|
|
scheme. */
|
|
|
|
if(atoi(vals[0])>1)
|
|
|
|
pgpkeystr="pgpKeyV2";
|
|
|
|
|
|
|
|
ldap_value_free(vals);
|
|
|
|
}
|
|
|
|
|
|
|
|
ldap_msgfree(si_res);
|
|
|
|
}
|
|
|
|
|
|
|
|
return LDAP_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2002-10-14 19:02:11 +00:00
|
|
|
int
|
|
|
|
main(int argc,char *argv[])
|
2002-06-29 13:31:13 +00:00
|
|
|
{
|
|
|
|
int port=0,arg,err,action=-1,ret=KEYSERVER_INTERNAL_ERROR;
|
2004-02-19 15:09:14 +00:00
|
|
|
char line[MAX_LINE];
|
2004-02-19 20:09:12 +00:00
|
|
|
int version,failed=0,use_ssl=0,use_tls=0;
|
2002-06-29 13:31:13 +00:00
|
|
|
struct keylist *keylist=NULL,*keyptr=NULL;
|
|
|
|
|
|
|
|
console=stderr;
|
|
|
|
|
2002-10-24 22:33:22 +00:00
|
|
|
while((arg=getopt(argc,argv,"hVo:"))!=-1)
|
2002-06-29 13:31:13 +00:00
|
|
|
switch(arg)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
case 'h':
|
|
|
|
fprintf(console,"-h\thelp\n");
|
2002-10-24 22:33:22 +00:00
|
|
|
fprintf(console,"-V\tversion\n");
|
2002-06-29 13:31:13 +00:00
|
|
|
fprintf(console,"-o\toutput to this file\n");
|
|
|
|
return KEYSERVER_OK;
|
|
|
|
|
2002-10-24 22:33:22 +00:00
|
|
|
case 'V':
|
2002-11-18 00:43:33 +00:00
|
|
|
fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
|
2002-10-24 22:33:22 +00:00
|
|
|
return KEYSERVER_OK;
|
|
|
|
|
2002-06-29 13:31:13 +00:00
|
|
|
case 'o':
|
|
|
|
output=fopen(optarg,"w");
|
|
|
|
if(output==NULL)
|
|
|
|
{
|
|
|
|
fprintf(console,"gpgkeys: Cannot open output file \"%s\": %s\n",
|
|
|
|
optarg,strerror(errno));
|
|
|
|
return KEYSERVER_INTERNAL_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(argc>optind)
|
|
|
|
{
|
|
|
|
input=fopen(argv[optind],"r");
|
|
|
|
if(input==NULL)
|
|
|
|
{
|
|
|
|
fprintf(console,"gpgkeys: Cannot open input file \"%s\": %s\n",
|
|
|
|
argv[optind],strerror(errno));
|
|
|
|
return KEYSERVER_INTERNAL_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(input==NULL)
|
|
|
|
input=stdin;
|
|
|
|
|
|
|
|
if(output==NULL)
|
|
|
|
output=stdout;
|
|
|
|
|
|
|
|
/* Get the command and info block */
|
|
|
|
|
|
|
|
while(fgets(line,MAX_LINE,input)!=NULL)
|
|
|
|
{
|
|
|
|
char commandstr[7];
|
|
|
|
char optionstr[30];
|
2004-02-19 20:09:12 +00:00
|
|
|
char schemestr[80];
|
2002-06-29 13:31:13 +00:00
|
|
|
char hash;
|
|
|
|
|
|
|
|
if(line[0]=='\n')
|
|
|
|
break;
|
|
|
|
|
|
|
|
if(sscanf(line,"%c",&hash)==1 && hash=='#')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if(sscanf(line,"COMMAND %6s\n",commandstr)==1)
|
|
|
|
{
|
|
|
|
commandstr[6]='\0';
|
|
|
|
|
|
|
|
if(strcasecmp(commandstr,"get")==0)
|
|
|
|
action=GET;
|
|
|
|
else if(strcasecmp(commandstr,"send")==0)
|
|
|
|
action=SEND;
|
|
|
|
else if(strcasecmp(commandstr,"search")==0)
|
|
|
|
action=SEARCH;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(sscanf(line,"HOST %79s\n",host)==1)
|
|
|
|
{
|
|
|
|
host[79]='\0';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(sscanf(line,"PORT %9s\n",portstr)==1)
|
|
|
|
{
|
|
|
|
portstr[9]='\0';
|
|
|
|
port=atoi(portstr);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2004-02-19 20:09:12 +00:00
|
|
|
if(sscanf(line,"SCHEME %79s\n",schemestr)==1)
|
|
|
|
{
|
|
|
|
schemestr[79]='\0';
|
|
|
|
if(strcasecmp(schemestr,"ldaps")==0)
|
|
|
|
{
|
|
|
|
port=636;
|
|
|
|
use_ssl=1;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2002-06-29 13:31:13 +00:00
|
|
|
if(sscanf(line,"VERSION %d\n",&version)==1)
|
|
|
|
{
|
2002-10-14 19:02:11 +00:00
|
|
|
if(version!=KEYSERVER_PROTO_VERSION)
|
2002-06-29 13:31:13 +00:00
|
|
|
{
|
|
|
|
ret=KEYSERVER_VERSION_ERROR;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(sscanf(line,"OPTION %29s\n",optionstr)==1)
|
|
|
|
{
|
|
|
|
int no=0;
|
|
|
|
char *start=&optionstr[0];
|
|
|
|
|
|
|
|
optionstr[29]='\0';
|
|
|
|
|
|
|
|
if(strncasecmp(optionstr,"no-",3)==0)
|
|
|
|
{
|
|
|
|
no=1;
|
|
|
|
start=&optionstr[3];
|
|
|
|
}
|
|
|
|
|
|
|
|
if(strcasecmp(start,"verbose")==0)
|
|
|
|
{
|
|
|
|
if(no)
|
|
|
|
verbose--;
|
|
|
|
else
|
|
|
|
verbose++;
|
|
|
|
}
|
|
|
|
else if(strcasecmp(start,"include-disabled")==0)
|
|
|
|
{
|
|
|
|
if(no)
|
|
|
|
include_disabled=0;
|
|
|
|
else
|
|
|
|
include_disabled=1;
|
|
|
|
}
|
|
|
|
else if(strcasecmp(start,"include-revoked")==0)
|
|
|
|
{
|
|
|
|
if(no)
|
|
|
|
include_revoked=0;
|
|
|
|
else
|
|
|
|
include_revoked=1;
|
|
|
|
}
|
|
|
|
else if(strcasecmp(start,"include-subkeys")==0)
|
|
|
|
{
|
|
|
|
if(no)
|
|
|
|
include_subkeys=0;
|
|
|
|
else
|
|
|
|
include_subkeys=1;
|
|
|
|
}
|
2004-02-19 20:09:12 +00:00
|
|
|
else if(strncasecmp(start,"tls",3)==0)
|
|
|
|
{
|
|
|
|
if(no)
|
|
|
|
use_tls=0;
|
|
|
|
else if(start[3]=='=')
|
|
|
|
{
|
|
|
|
if(strcasecmp(&start[4],"no")==0)
|
|
|
|
use_tls=0;
|
|
|
|
else if(strcasecmp(&start[4],"try")==0)
|
|
|
|
use_tls=1;
|
|
|
|
else if(strcasecmp(&start[4],"warn")==0)
|
|
|
|
use_tls=2;
|
|
|
|
else if(strcasecmp(&start[4],"require")==0)
|
|
|
|
use_tls=3;
|
|
|
|
else
|
|
|
|
use_tls=1;
|
|
|
|
}
|
|
|
|
else if(start[3]=='\0')
|
|
|
|
use_tls=1;
|
|
|
|
}
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If it's a GET or a SEARCH, the next thing to come in is the
|
|
|
|
keyids. If it's a SEND, then there are no keyids. */
|
|
|
|
|
|
|
|
if(action==SEND)
|
|
|
|
while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
|
|
|
|
else if(action==GET || action==SEARCH)
|
|
|
|
{
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
struct keylist *work;
|
|
|
|
|
|
|
|
if(fgets(line,MAX_LINE,input)==NULL)
|
|
|
|
break;
|
|
|
|
else
|
|
|
|
{
|
2004-01-12 04:09:37 +00:00
|
|
|
if(line[0]=='\n' || line[0]=='\0')
|
2002-06-29 13:31:13 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
work=malloc(sizeof(struct keylist));
|
|
|
|
if(work==NULL)
|
|
|
|
{
|
|
|
|
fprintf(console,"gpgkeys: out of memory while "
|
|
|
|
"building key list\n");
|
2002-10-09 02:03:22 +00:00
|
|
|
ret=KEYSERVER_NO_MEMORY;
|
2002-06-29 13:31:13 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(work->str,line);
|
|
|
|
|
|
|
|
/* Trim the trailing \n */
|
|
|
|
work->str[strlen(line)-1]='\0';
|
|
|
|
|
|
|
|
work->next=NULL;
|
|
|
|
|
|
|
|
/* Always attach at the end to keep the list in proper
|
|
|
|
order for searching */
|
|
|
|
if(keylist==NULL)
|
|
|
|
keylist=work;
|
|
|
|
else
|
|
|
|
keyptr->next=work;
|
|
|
|
|
|
|
|
keyptr=work;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf(console,"gpgkeys: no keyserver command specified\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send the response */
|
|
|
|
|
2002-10-14 19:02:11 +00:00
|
|
|
fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
|
2002-06-29 13:31:13 +00:00
|
|
|
fprintf(output,"PROGRAM %s\n\n",VERSION);
|
|
|
|
|
|
|
|
if(verbose>1)
|
|
|
|
{
|
|
|
|
fprintf(console,"Host:\t\t%s\n",host);
|
|
|
|
if(port)
|
|
|
|
fprintf(console,"Port:\t\t%d\n",port);
|
|
|
|
fprintf(console,"Command:\t%s\n",action==GET?"GET":
|
|
|
|
action==SEND?"SEND":"SEARCH");
|
|
|
|
}
|
|
|
|
|
2002-10-14 19:02:11 +00:00
|
|
|
/* Note that this tries all A records on a given host (or at least,
|
|
|
|
OpenLDAP does). */
|
2002-06-29 13:31:13 +00:00
|
|
|
ldap=ldap_init(host,port);
|
|
|
|
if(ldap==NULL)
|
|
|
|
{
|
2002-10-09 02:03:22 +00:00
|
|
|
fprintf(console,"gpgkeys: internal LDAP init error: %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
fail_all(keylist,action,KEYSERVER_INTERNAL_ERROR);
|
2002-06-29 13:31:13 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2004-02-19 20:09:12 +00:00
|
|
|
if(use_ssl)
|
|
|
|
{
|
|
|
|
if(!real_ldap)
|
|
|
|
{
|
|
|
|
fprintf(console,"gpgkeys: unable to make SSL connection: %s\n",
|
|
|
|
"not supported by the NAI LDAP keyserver");
|
|
|
|
fail_all(keylist,action,KEYSERVER_INTERNAL_ERROR);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#if defined(LDAP_OPT_X_TLS_HARD) && defined(HAVE_LDAP_SET_OPTION)
|
|
|
|
int ssl=LDAP_OPT_X_TLS_HARD;
|
|
|
|
err=ldap_set_option(ldap,LDAP_OPT_X_TLS,&ssl);
|
|
|
|
if(err!=LDAP_SUCCESS)
|
|
|
|
{
|
|
|
|
fprintf(console,"gpgkeys: unable to make SSL connection: %s\n",
|
|
|
|
ldap_err2string(err));
|
|
|
|
fail_all(keylist,action,ldap_err_to_gpg_err(err));
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
fprintf(console,"gpgkeys: unable to make SSL connection: %s\n",
|
|
|
|
"not built with LDAPS support");
|
|
|
|
fail_all(keylist,action,KEYSERVER_INTERNAL_ERROR);
|
|
|
|
goto fail;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-02-19 21:32:15 +00:00
|
|
|
if((err=find_basekeyspacedn()))
|
|
|
|
{
|
|
|
|
fprintf(console,"gpgkeys: unable to retrieve LDAP base: %s\n",
|
|
|
|
ldap_err2string(err));
|
|
|
|
fail_all(keylist,action,ldap_err_to_gpg_err(err));
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2004-02-19 20:09:12 +00:00
|
|
|
/* use_tls: 0=don't use, 1=try silently to use, 2=try loudly to use,
|
|
|
|
3=force use. */
|
|
|
|
if(use_tls)
|
|
|
|
{
|
2004-02-19 21:32:15 +00:00
|
|
|
if(!real_ldap)
|
2004-02-19 20:09:12 +00:00
|
|
|
{
|
|
|
|
if(use_tls>=2)
|
|
|
|
fprintf(console,"gpgkeys: unable to start TLS: %s\n",
|
|
|
|
"not supported by the NAI LDAP keyserver");
|
|
|
|
if(use_tls==3)
|
|
|
|
{
|
|
|
|
fail_all(keylist,action,KEYSERVER_INTERNAL_ERROR);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#if defined(HAVE_LDAP_START_TLS_S) && defined(HAVE_LDAP_SET_OPTION)
|
|
|
|
int ver=LDAP_VERSION3;
|
|
|
|
|
|
|
|
err=LDAP_SUCCESS;
|
|
|
|
|
|
|
|
err=ldap_set_option(ldap,LDAP_OPT_PROTOCOL_VERSION,&ver);
|
|
|
|
if(err==LDAP_SUCCESS)
|
|
|
|
err=ldap_start_tls_s(ldap,NULL,NULL);
|
|
|
|
|
|
|
|
if(err!=LDAP_SUCCESS && use_tls>=2)
|
|
|
|
{
|
|
|
|
fprintf(console,"gpgkeys: unable to start TLS: %s\n",
|
|
|
|
ldap_err2string(err));
|
|
|
|
/* Are we forcing it? */
|
|
|
|
if(use_tls==3)
|
|
|
|
{
|
|
|
|
fail_all(keylist,action,ldap_err_to_gpg_err(err));
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(verbose>1)
|
|
|
|
fprintf(console,"gpgkeys: TLS started successfully.\n");
|
|
|
|
#else
|
|
|
|
if(use_tls>=2)
|
|
|
|
fprintf(console,"gpgkeys: unable to start TLS: %s\n",
|
|
|
|
"not built with TLS support");
|
|
|
|
if(use_tls==3)
|
|
|
|
{
|
|
|
|
fail_all(keylist,action,KEYSERVER_INTERNAL_ERROR);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-02-19 21:32:15 +00:00
|
|
|
/* The LDAP keyserver doesn't require this, but it might be useful
|
|
|
|
if someone stores keys on a V2 LDAP server somewhere. (V3
|
|
|
|
doesn't require a bind). */
|
|
|
|
|
2002-06-29 13:31:13 +00:00
|
|
|
err=ldap_simple_bind_s(ldap,NULL,NULL);
|
|
|
|
if(err!=0)
|
|
|
|
{
|
|
|
|
fprintf(console,"gpgkeys: internal LDAP bind error: %s\n",
|
|
|
|
ldap_err2string(err));
|
2002-10-09 02:03:22 +00:00
|
|
|
fail_all(keylist,action,ldap_err_to_gpg_err(err));
|
2002-06-29 13:31:13 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(action)
|
|
|
|
{
|
|
|
|
case GET:
|
|
|
|
keyptr=keylist;
|
|
|
|
|
|
|
|
while(keyptr!=NULL)
|
|
|
|
{
|
2002-09-24 20:17:52 +00:00
|
|
|
if(get_key(keyptr->str)!=KEYSERVER_OK)
|
2002-06-29 13:31:13 +00:00
|
|
|
failed++;
|
|
|
|
|
|
|
|
keyptr=keyptr->next;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SEND:
|
|
|
|
{
|
2002-09-24 20:17:52 +00:00
|
|
|
int eof=0;
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
do
|
|
|
|
{
|
2004-02-22 00:08:53 +00:00
|
|
|
if(real_ldap)
|
|
|
|
{
|
|
|
|
if(send_key(&eof)!=KEYSERVER_OK)
|
|
|
|
failed++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(send_key_keyserver(&eof)!=KEYSERVER_OK)
|
|
|
|
failed++;
|
|
|
|
}
|
2002-06-29 13:31:13 +00:00
|
|
|
}
|
2002-09-24 20:17:52 +00:00
|
|
|
while(!eof);
|
2002-06-29 13:31:13 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SEARCH:
|
|
|
|
{
|
|
|
|
char *searchkey=NULL;
|
|
|
|
int len=0;
|
|
|
|
|
|
|
|
/* To search, we stick a * in between each key to search for.
|
|
|
|
This means that if the user enters words, they'll get
|
|
|
|
"enters*words". If the user "enters words", they'll get
|
|
|
|
"enters words" */
|
|
|
|
|
|
|
|
keyptr=keylist;
|
|
|
|
while(keyptr!=NULL)
|
|
|
|
{
|
|
|
|
len+=strlen(keyptr->str)+1;
|
|
|
|
keyptr=keyptr->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
searchkey=malloc(len+1);
|
|
|
|
if(searchkey==NULL)
|
2002-09-24 20:17:52 +00:00
|
|
|
{
|
|
|
|
ret=KEYSERVER_NO_MEMORY;
|
2002-10-09 02:03:22 +00:00
|
|
|
fail_all(keylist,action,KEYSERVER_NO_MEMORY);
|
2002-09-24 20:17:52 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
searchkey[0]='\0';
|
|
|
|
|
|
|
|
keyptr=keylist;
|
|
|
|
while(keyptr!=NULL)
|
|
|
|
{
|
|
|
|
strcat(searchkey,keyptr->str);
|
|
|
|
strcat(searchkey,"*");
|
|
|
|
keyptr=keyptr->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Nail that last "*" */
|
2004-01-12 04:09:37 +00:00
|
|
|
if(*searchkey)
|
|
|
|
searchkey[strlen(searchkey)-1]='\0';
|
2002-06-29 13:31:13 +00:00
|
|
|
|
2002-09-24 20:17:52 +00:00
|
|
|
if(search_key(searchkey)!=KEYSERVER_OK)
|
|
|
|
failed++;
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
free(searchkey);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!failed)
|
|
|
|
ret=KEYSERVER_OK;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
|
|
|
while(keylist!=NULL)
|
|
|
|
{
|
|
|
|
struct keylist *current=keylist;
|
|
|
|
keylist=keylist->next;
|
|
|
|
free(current);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(input!=stdin)
|
|
|
|
fclose(input);
|
|
|
|
|
|
|
|
if(output!=stdout)
|
|
|
|
fclose(output);
|
|
|
|
|
|
|
|
if(ldap!=NULL)
|
|
|
|
ldap_unbind_s(ldap);
|
|
|
|
|
|
|
|
free(basekeyspacedn);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|